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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions