Command Injection
Prerequisites¶
- INSECURE-threat-intel-app running; signed in at http://localhost:8081.
- Burp Suite (Burp basics).
- Optional:
docker compose -f docker/docker-compose.yml logs appto correlate server-side execution.
Likeliness meter¶
Moderate — Less frequent than SQLi/XSS but critical where apps call shells with user input (file hashing/AV scans, archive handling, DNS/WHOIS lookups, backups, PDF/image conversion).
Overview¶
Command injection (OS command injection) occurs when user input is passed to the operating system through a shell (or a function that invokes a shell) without strict validation. Shell metacharacters let the attacker terminate the intended command and chain a new one:
| Character | Typical effect |
|---|---|
; |
Run another command after the first |
&& / \|\| |
Run if previous succeeded / failed |
\| |
Pipe output into another command |
` / $() |
Command substitution |
The impact is remote code execution as the Unix user running the web app — read files, pivot, exfiltrate data — within the container or host boundary.
The vulnerable feature in this lab is Tools → Ingest artifact integrity check (/tools?artifact=). A CTI platform stages incoming feed/sample files in an ingestion directory, and operators verify each file's SHA-256 before importing it. The server builds the command by string-concatenating the artifact name and runs it through a shell:
Because <artifact> is attacker-controlled and unsanitized, you can break out of the sha256sum argument and run your own commands. The real shell output (your id, the container's hostname) comes straight back in the HTTP response — nothing is simulated.
Learning objectives¶
- Spot features that shell out to system utilities (here: a file-hashing integrity check).
- Establish a benign baseline in Burp before injecting metacharacters.
- Prove arbitrary command execution with controlled payloads (
id,hostname). - Document in your lab report.
Attacker methodology¶
Step 1 — Map the attack surface (and skip non-shell features)¶
Browse the app. Ask: “Where does the server run a system command on my input?”
| Area | Command injection? |
|---|---|
| Dashboard charts | No — data from Postgres |
| SQLi / XSS labs | No — different bug classes |
| Tools → Ingest artifact integrity check | Yes — artifact parameter is hashed via a shell |
Burp HTTP history: open http://localhost:8081/tools and watch for GET /tools?artifact=… requests.
Step 2 — Baseline: a legitimate integrity check¶
On Tools, the page lists staged artifacts (e.g. feed-2026-01-05.json, iocs-export.csv). Click one, or submit its name:
You should see a real SHA-256 line, e.g.:
Burp:
- Find
GET /tools?artifact=feed-2026-01-05.json. - Send to Repeater — keep this tab as your “clean” comparison response.
Step 3 — Probe: special characters without full exploit yet¶
Attackers send one metacharacter to see if filtering or errors appear. In Repeater, try:
If the response changes or shows a shell error, note it. If the hash still returns normally, continue — filters may be absent.
Dead end example: trying this on the SQLi search box — input goes to PostgreSQL, not /bin/sh; no OS output. Note that distinction.
Step 4 — Prove command chaining (; or &&)¶
Append a command after the filename:
or:
Repeater → Send.
Expected: the response contains the real output of your command alongside (or instead of) the hash:
b1946ac92492d2347c6235b4d2611184… /tmp/cti-ingest/feed-2026-01-05.json
uid=0(root) gid=0(root) groups=0(root)
or, for hostname, the container's actual hostname (e.g. a1b2c3d4e5f6). The “Found Command Injection” banner appears when id-style output is detected.
You have shown the server built a shell command like:
Step 5 — Expand proof (still in the lab)¶
Useful, low-noise commands that return obviously-real data:
If root:x:0:0 lines appear, you have file read via RCE. With no filename at all you can drop the prefix entirely — e.g. ; id — because the artifact name is concatenated raw.
Step 6 — Correlate with server logs (optional)¶
From the project directory:
You may not see the shell command logged — production apps rarely log full argv. Your Burp response is still primary evidence.
Step 7 — Report checklist¶
| Field | Example |
|---|---|
| URL | /tools?artifact= |
| Parameter | artifact |
| Baseline | feed-2026-01-05.json → SHA-256 line |
| Payload | feed-2026-01-05.json; id |
| Impact | Arbitrary OS command execution as app user in container (real id/hostname output) |
| Root cause | Shell invocation with unsanitized input (exec + string concatenation) |
| Remediation | Use execFile/native hashing with no shell; allowlist artifact names |
Attach Burp Repeater before/after responses.
Remediation¶
- Avoid shells — hash files with a native API (Node
crypto.createHash('sha256')) or call the program with an argument array (execFile('sha256sum', [path])), nevershell: truewith user input. - Allowlist artifact names (e.g.
^[A-Za-z0-9._-]+$) and resolve them inside the ingest directory so path traversal and metacharacters are rejected. - Run with minimal service-account privileges and isolated containers.