mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
fix(approval): catch sudo with stdin/askpass/shell privilege flags
Adds the only #17873 category not covered by the in-flight PRs #17962 (briandevans, reverse shell + download-execute) and #7993 (SHL0MS, credential reads + curl/wget exfiltration): sudo invocations that an LLM-driven agent can drive without TTY interaction. The agent has no TTY, so the sudo forms that succeed without human involvement are those reading the password from stdin (`-S` / `--stdin`) or via an askpass helper (`-A` / `--askpass`). The shell-launch (`-s`) and list-privileges (`-a`) flags are also gated since they are privilege-relevant invocations the agent can chain after acquiring the password (e.g. read SUDO_PASSWORD from .env -> sudo -S -s -> root shell). Plain `sudo cmd` (no flag) is TTY-bound and excluded. Two patterns: 1. Direct flag: `\bsudo\b[^;|&\n]*?\s+(?:-s\b|--stdin\b|-a\b|--askpass\b)` The lazy `[^;|&\n]*?` consumes flag-arguments without spanning command separators, so `sudo -u root -S whoami` matches (a textbook offensive form that a strict `(?:\s+-[^\s]+)*` "leading flags only" pattern would have missed because `root` is a flag-value not a flag). 2. Combined short flags: `\bsudo\b[^;|&\n]*?\s+-[a-z]*[sa][a-z]*\b` Catches packed forms like `sudo -nS id` where multiple flags share a single `-X` token. `_normalize_command_for_detection` lowercases input before pattern matching (tools/approval.py:340), so case variants of S/s and A/a collapse — both letter-pairs are gated since each is a privilege- relevant invocation. Tests: 21 new cases in TestDetectSudoStdin (12 positive covering all flag-order permutations including herestring source and printf-piped forms; 9 negative including TTY-bound `sudo whoami`, interactive `sudo -i`, env-var reference `$SUDO_USER`, doc lookup `man sudo`, package install, and the `pseudosudo` word-boundary edge case). Empirical coverage: 11/11 attacks matched, 0/10 false positives. Refs: #17873 category 4. Adjacent: #17962 (reverse shell + download- execute), #7993 (credential reads + curl/wget exfiltration). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9520a1ccdf
commit
976d8e27ad
2 changed files with 156 additions and 0 deletions
|
|
@ -368,6 +368,25 @@ DANGEROUS_PATTERNS = [
|
|||
# a script is first made executable then immediately run. The script
|
||||
# content may contain dangerous commands that individual patterns miss.
|
||||
(r'\bchmod\s+\+x\b.*[;&|]+\s*\./', "chmod +x followed by immediate execution"),
|
||||
# Sudo with stdin / askpass / shell / list-privs flags. An LLM-driven
|
||||
# agent has no TTY, so sudo invocations that succeed without human
|
||||
# interaction are those reading the password from stdin (-S/--stdin)
|
||||
# or via an askpass helper (-A/--askpass). The shell-launch (-s) and
|
||||
# list-privileges (-a) flags are also gated since they are
|
||||
# privilege-relevant invocations the agent can chain after acquiring
|
||||
# the password (e.g. read SUDO_PASSWORD from .env -> sudo -S -s ->
|
||||
# root shell). Plain `sudo cmd` (no flag) is TTY-bound and excluded.
|
||||
# `_normalize_command_for_detection` lowercases input before pattern
|
||||
# matching, so case variants of S/s and A/a collapse — both forms
|
||||
# are gated below. Lazy `[^;|&\n]*?` allows flag arguments (e.g.
|
||||
# `sudo -u root -S whoami`) without spanning command separators. See
|
||||
# #17873 category 4.
|
||||
(r'\bsudo\b[^;|&\n]*?\s+(?:-s\b|--stdin\b|-a\b|--askpass\b)',
|
||||
"sudo with privilege flag (stdin/askpass/shell/list)"),
|
||||
# Combined short-flag form: -nS, -ns, -sa, -las — sudo flags packed
|
||||
# into a single -X token. Catches the same threat class.
|
||||
(r'\bsudo\b[^;|&\n]*?\s+-[a-z]*[sa][a-z]*\b',
|
||||
"sudo with combined-flag privilege escalation"),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue