Skip to content

python-fire: (1) PAGER+shell=True RCE in console_io.More (2) bash $(...) runs when sourcing --completion from maliciously named .py file. PoC verified. #665

@tikipiya

Description

@tikipiya

Summary

Two related issues in python-fire (library + python -m fire entrypoint): (1) OS command injection when an external pager is spawned; (2) arbitrary shell command execution when a user sources a generated Bash completion script, if the CLI name (often derived from a .py filename) contains Bash command substitution or other shell metacharacters.

Repository: https://github.com/google/python-fire


Issue A: PAGER + subprocess.Popen(..., shell=True)

File: fire/console/console_io.py, function More(), approximately lines 81–100.

Behavior: When check_pager is true (default), stdin/stdout are TTYs, and PAGER is set, the value from os.environ['PAGER'] is passed to:

subprocess.Popen(pager, stdin=subprocess.PIPE, shell=True)

Because shell=True, the string is interpreted by /bin/sh. Metacharacters (;, |, `, $(), etc.) allow arbitrary commands to run in the same security context as the Fire-based process.

Trigger: Any code path that calls console_io.More() with the default pager behavior—e.g. fire.core.Display() used for help text, traces, and other paged output—when the process has a malicious or attacker-controlled PAGER and an interactive terminal.

Verification (PoC): With PAGER='touch /tmp/fire_verify_pager', run under a pseudo-TTY (e.g. script -qefc 'python -c "from fire.console import console_io; import sys; console_io.More(\"x\", sys.stdout)"' /dev/null). The side-effect file appears, confirming shell interpretation of PAGER.


Issue B: Bash completion script — unsanitized name / identifier

File: fire/completion.py, function _BashScript(), template bash_completion_template and .format(...) around lines 179–187.

Behavior: The Bash script embeds name and identifier with only a weak transform:

identifier = name.replace('/', '').replace('.', '').replace(',', '')

Characters such as $, (, ), `, ;, newlines, etc. are not removed. The template includes lines such as:

  • _complete-{identifier}()
  • complete -F _complete-{identifier} {command}

with command=name. When the generated script is sourced by Bash, command substitution in identifier or name (e.g. $(touch marker)) is evaluated.

Trigger: python -m fire sets name from os.path.basename(path) (fire/__main__.py, import_from_file_path). A file named like $(touch _marker).py therefore yields a completion script that runs touch (or worse) when the victim runs source on the output of -- --completion.

Verification (PoC): Create a minimal module file literally named $(touch _fire_verify_completion_marker).py, run python -m fire '<path>' -- --completion > comp.sh, then bash -c 'source comp.sh'. The marker file is created (Bash may also error on an invalid function name, but substitution still runs).

Note: A subshell payload must not contain / in the basename (invalid filename on Unix); use e.g. $(touch _marker) with a relative target.


CWE mapping

  • Issue A: CWE-78 (OS command injection)
  • Issue B: CWE-78 / insufficient neutralization of shell metacharacters in generated shell code

Who can exploit

  • Issue A: Anyone who can control the environment of a process that runs Fire and reaches More() on a TTY with PAGER set (e.g. misconfigured services, wrappers, CI jobs, compromised .env, multi-tenant job runners). The user running the CLI can also set PAGER themselves; the higher risk is untrusted or inherited environment for automated or privileged contexts.
  • Issue B: An attacker who can place or name a .py file on disk (or otherwise influence the name passed to fire.Fire(..., name=...)) and convince a victim to generate and source Bash completion from that module (e.g. cloned repo, malicious path, social engineering). Supply-chain / “run this install script” scenarios are plausible.

What the attacker gains

  • Issue A: Arbitrary code execution as the Fire process user: read/write secrets the process can access, lateral movement on the host, data destruction (CIA triad: High).
  • Issue B: Arbitrary code execution as the user who sources the completion script in Bash—typically the developer’s or operator’s interactive account.

Prerequisites / limits

  • Issue A requires an interactive TTY for the default pager path (IsInteractive(output=True)); non-TTY falls back to writing without PAGER.
  • Issue B requires the victim to execute the generated script in Bash (e.g. source); reading the file alone is not enough.

Suggested severity (informal)

  • Issue A: often assessed High locally (e.g. CVSS ~8.x) when env is attacker-influenced; lower if only self-controlled env.
  • Issue B: High when sourcing untrusted completion is realistic; depends on social/engineering and workflow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions