Command Injection
Detects potential command injection vulnerabilities in shell/command/raw tasks
27 rules in command_injection.yml
CRITICAL: 2 | HIGH: 19 | MEDIUM: 6
| Rule ID | Severity | Title | Description | Refs |
|---|---|---|---|---|
docker_ | CRITICAL | Docker Privileged with Host Access | Docker command runs privileged container with host network access | |
raw_ | CRITICAL | Raw Module with Encoded PowerShell | Uses Ansible raw module to execute base64-encoded PowerShell commands, hiding the actual payload | |
curl_ | HIGH | Curl with Hardcoded Credentials | Task invokes curl/wget/HTTPie with hardcoded basic-auth credentials, a netrc file, or a -K config file containing user:password. The pair is visible in process listings and Ansible’s verbose output, defeating any vault elsewhere. | |
database_ | HIGH | Database Command with Embedded Password | mysql/psql command embeds -p’ | |
decode_ | HIGH | Decode and Pipe to Shell | Task decodes data and pipes to shell which is a common attack vector | |
download_ | HIGH | Download and Pipe to Shell | Task pipes wget/curl output directly into bash/sh/zsh (curl ... | bash). The remote payload runs unverified and any MitM or compromised mirror executes attacker code as the playbook user. | |
eval_ | HIGH | Shell eval Builtin - Arbitrary Code Execution from a String | Uses the shell eval builtin - which parses its argument as a shell command and executes it, collapsing the argv/shell distinction. The rule matches eval ONLY when it appears at the start of a shell command or immediately after a shell separator (;, &&, ||, \n, start of line). Splunk’s SPL | eval pipeline operator (splunk search '| eval host="*"'), MySQL’s eval(), and every other in-DSL eval are NOT the shell builtin and are NOT flagged. | |
interpreter_ | HIGH | Inline Interpreter Payload Interpolates Jinja or Imports Exec-Family Primitives | A shell / command / raw task invokes python / python3 / perl / ruby with -c / -e and an inline source string that is EITHER (a) interpolating a Jinja {{ var }} into the code - controller bytes are injected into live interpreter source at execution time - OR (b) imports one of the offensive-primitive modules os / subprocess / socket / pty / ctypes / Open3 / IO.popen (for ruby) / system / exec / backticks (for perl) AND calls an exec-family function. Pure literal inline code that doesn’t touch those primitives (python3 -c 'print(42)', perl -e 'print sort @ARGV') is a normal ops idiom and is NOT flagged. | |
password_ | HIGH | Password in Command Line | Task passes -p’ | |
powershell_ | HIGH | PowerShell Encoded Command | Task uses PowerShell with encoded command which can hide malicious code | |
process_ | HIGH | Process Substitution | Shell/command/raw task uses Bash process substitution <(...) to inline a command’s output. Process substitution executes arbitrary subshells and is a common command-injection sink. | |
secret_ | HIGH | Secret-Shaped Shell Variable Interpolated Into Command Argv | A shell task interpolates a shell variable whose name suggests secret material into an argv-revealing flag (-p, –patch, –data, –from-literal, –password, –token, –key, –secret-string, –set). Argv lands in /proc/ | |
shell_ | HIGH | Inline Shell -c Payload Interpolates Jinja or Evals Remote Script | A shell / command / raw task invokes sh -c / bash -c / zsh -c / ksh -c (or the Debian-default /bin/sh which resolves to dash) with an inline payload that is EITHER (a) interpolating a Jinja {{ var }} into the script body - controller-side bytes flowing into a re-parsing shell, classic injection shape - OR (b) eval-ing a remote script via "$(curl ...)" / wget ... - the canonical supply-chain one-liner exploited by install.sh typosquats and the ohmyzsh / nvm / rustup bootstrap pattern. Pure literal compound commands (bash -c 'mkdir -p /opt && cp ...') are a normal operator idiom and are NOT flagged. Container-exec shapes (podman exec <c> bash -c '...', docker exec, kubectl exec -- bash -c, nerdctl exec, lxc exec, incus exec, buildah run, chroot, nsenter) are also NOT flagged - those MUST use bash -c to run a compound command inside the target namespace. | |
shell_ | HIGH | Shell Pipe to Interpreter | Task pipes output to shell interpreter which can lead to command injection | |
subshell_ | HIGH | Subshell / Command Substitution Interpolates a Jinja Variable | A shell/command/raw task uses $(...) or backticks and interpolates a Jinja {{ var }} into the substitution. If the rendered variable contains shell metachars the substitution becomes an injection primitive (the inner shell re-parses the value, so even | quote on the outer command doesn’t protect the inner context). Plain command substitution WITHOUT a Jinja interpolation is a normal shell idiom ($(sha512sum file), $(basename $(dirname "$f"))) and is NOT flagged. | |
systemctl_ | HIGH | Systemctl with User Input | Systemctl command uses template variables which may be unsafe | |
user_ | HIGH | Direct User Input Execution | Task directly executes user input which is extremely dangerous | |
wget_ | HIGH | Wget Pipe to Shell | Task downloads content and pipes to shell which is dangerous | |
windows_ | HIGH | Windows CMD with /c Flag | win_shell or win_command task explicitly invokes cmd.exe /c, which evaluates the rest of the line via the legacy Windows command interpreter and reintroduces cmd metacharacter parsing. | |
windows_ | HIGH | Windows Command Chaining | win_shell or win_command task chains commands with ;, &&, or || in the same string. Chaining prevents Ansible from reasoning about return codes and lets a follow-on command run after a failure. | |
windows_ | HIGH | Windows Shell Pipe to Invoke-Expression | Windows task pipes output to Invoke-Expression which can execute arbitrary code | |
command_ | MEDIUM | Command Chaining with Template Interpolation | A shell: or raw: task chains commands with ; / && / || AND interpolates a Jinja variable into the chain. If the variable renders to a value containing shell metachars, the chain becomes an injection surface (e.g. rm -rf /tmp/{{ name }} && restart) - an attacker-controlled name of ;; curl http://evil lets the chain pivot. Pure command chaining without interpolation is a normal automation idiom (podman stop foo || true, mkdir -p /opt/app && cd /opt/app && ./setup.sh) and is NOT flagged. command: is EXCLUDED because it does execve(argv[]) - ; / && / || become literal argv tokens and are never interpreted by a shell. | |
curl_ | MEDIUM | Curl with Multiple Template Variables | Task uses curl with multiple template variables which may be unsafe | |
environment_ | MEDIUM | Shell Task References Attacker-Influenced Environment Variable | A shell / command / raw task dereferences an environment variable (${VAR} / $VAR) whose name matches a well-known attacker-influenced source: CGI / reverse-proxy request headers (HTTP_*, QUERY_STRING, REQUEST_METHOD, REQUEST_URI, PATH_INFO, REMOTE_ADDR, REMOTE_USER), SSH forced-command inputs (SSH_ORIGINAL_COMMAND, SSH_CONNECTION), or sudo-preserved pass-through (SUDO_COMMAND). These values land inside the shell command unquoted, giving an attacker who controls the HTTP request / SSH key / sudo invocation arbitrary command execution. Locally-scoped shell loop variables (${name}, ${i}, ${f}) are NOT flagged - they are set earlier in the same shell body and are not attacker-influenced. | |
heredoc_ | MEDIUM | Heredoc with Template Variables | Task uses heredoc with template variables which may be unsafe | |
indirect_ | MEDIUM | Indirect Variable Expansion | Task uses indirect variable expansion which can be dangerous | |
secret_ | MEDIUM | Secret Manager Output Piped Through External Text Processor | A shell task fetches material from a secret store (aws secretsmanager get-secret-value, gcloud secrets versions access, az keyvault secret show, vault kv get, op read, bw get password) and pipes the result through jq, awk, sed, grep, cut, or tr. Even with no_log: true, the secret crosses argv and process memory of every command in the pipe; auditd execve records, bash -x, set -x, and strace -f all capture the value. Use the corresponding lookup plugin or query syntax built into the CLI so the value never leaves the privileged process. |