google / google/python-fire

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

Offen
#665 2 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
Vorherrschende Sprache
Python
Sterne
28.2k
Forks
1.5k
PR-Merge-Kennzahlen
Keine gemergten PRs in 30 T.

Beschreibung

## 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 '' -- --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.

Beitragsleitfaden

Beitragsleitfaden öffnen

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.