Skip to content

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 app to 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:

sha256sum /tmp/cti-ingest/<artifact>

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

  1. Spot features that shell out to system utilities (here: a file-hashing integrity check).
  2. Establish a benign baseline in Burp before injecting metacharacters.
  3. Prove arbitrary command execution with controlled payloads (id, hostname).
  4. 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 Yesartifact 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:

feed-2026-01-05.json

You should see a real SHA-256 line, e.g.:

b1946ac92492d2347c6235b4d2611184…  /tmp/cti-ingest/feed-2026-01-05.json

Burp:

  1. Find GET /tools?artifact=feed-2026-01-05.json.
  2. 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:

feed-2026-01-05.json;
feed-2026-01-05.json|

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:

feed-2026-01-05.json; id

or:

feed-2026-01-05.json && hostname

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:

sha256sum /tmp/cti-ingest/feed-2026-01-05.json; id

Step 5 — Expand proof (still in the lab)

Useful, low-noise commands that return obviously-real data:

feed-2026-01-05.json; whoami
feed-2026-01-05.json; uname -a
feed-2026-01-05.json; cat /etc/passwd

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:

docker compose -f docker/docker-compose.yml logs app --tail=20

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])), never shell: true with 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.