DedSec Application Security ·
A dynamic scan sends a payload, reads the response, and decides. For a large class of vulnerabilities that is enough: the injected value is reflected, an error message changes, a row appears that should not. For another class it is useless. The server-side request to an internal address is made and its result is thrown away. The injected shell command runs and its output goes nowhere the client can see. The XML parser resolves an external entity and returns the same document either way. The response is byte-for-byte what it would be on a safe server.
These are the blind vulnerabilities, and the only way to confirm most of them is to stop reading the response and start watching a second channel. That is out-of-band application security testing: you hand the target a unique address that points at infrastructure you control, and you wait to see whether the target contacts it. This post is about how that channel actually works, what a callback does and does not prove, and how its false positives differ from everything else a scanner reports.
Two techniques cover most non-blind findings, and both fail here for structural reasons.
Response diffing compares the application's output across payloads and looks for a difference that implies the payload was interpreted. It needs the vulnerability to change something the client can observe: reflected content, an error, a different row count, a redirect. A blind server-side request forgery issues a back-end request whose response is never returned to the front end, so there is nothing to diff. PortSwigger's definition is exactly this: the application can be induced to issue a back-end request to a supplied URL, but the response from that request is not returned in the application's response. Diffing sees two identical pages and reports nothing.
Timing infers a vulnerability from how long the response takes. For blind SQL injection you inject a conditional sleep; for blind command injection you inject a command that pings a loopback address for a fixed number of seconds to stretch the response. It works when it works, and it is noisy by nature. A single slow response proves little: shared database load, garbage collection, a cold cache, network jitter and a busy CI runner all produce the same delay, so a timing check has to repeat and threshold, and a scanner that sends a conditional delay against tens of thousands of parameters is deliberately injecting latency into a system it is also trying to measure. Worse, timing cannot distinguish a vulnerability that executed from one that executed asynchronously, on another thread, after the response was already sent. An asynchronous sink is invisible to both diffing and timing, because by the time the dangerous work happens the request is closed.
The out-of-band channel sidesteps all of it. It does not care whether the response changed or how long it took. It asks one question: did the target, or something acting for it, reach out to an address only this payload contained?
The mechanism has four parts: a unique address per payload, a server that is authoritative for that address across several protocols, a client that polls for interactions, and a correlation step that maps an interaction back to the request that caused it. Interactsh, the open-source OAST server and client library from ProjectDiscovery, is a concrete and auditable example, and the pieces below are drawn from its public documentation and source.
A unique address per payload. The client holds a correlation identifier, a random preamble of a fixed length by default. Each time it is asked for a new interaction address, it appends a fresh random nonce and the server's hostname, producing a subdomain like preamble-plus-nonce dot the-server-domain. The preamble is constant for a session and ties every address back to this client; the nonce makes each individual address unique, so two payloads sent to two parameters carry two different hostnames that still share one correlation prefix.
A server that is authoritative and listening. The OAST server is the authoritative DNS server for its domain: any resolver that chases the name ends up asking it, and it answers with its own address. It then runs listeners on the protocols a blind payload might use: DNS itself, HTTP and HTTPS, SMTP and LDAP, and, on a self-hosted instance with the relevant options enabled, SMB and FTP. When anything resolves or connects to one of the unique hostnames, the server records the protocol, the full hostname that was used, the query type for DNS, the raw request, the source address, and a timestamp.
A client that polls. The scanner does not receive a push. The client periodically asks the server for new interactions tied to its correlation identifier, by default every few seconds. On Interactsh, interactions returned to the client are encrypted to a key the client registered at startup. The server still receives each interaction in plaintext as it arrives, so using a shared public server means trusting its operator with whatever the target sends. Self-hosting removes that intermediary.
Correlation back to the exact request. This is the part that turns a callback into a finding. Because the scanner chose the hostname before it sent the payload, it already knows which request and which parameter that hostname went into. When an interaction arrives carrying that hostname, the scanner looks it up in its own table and attaches it to the originating request. Nuclei, which drives many out-of-band templates, embeds a placeholder in the outgoing request that is replaced at runtime with a unique interaction URL, and then matches on the interaction protocol that comes back, handling the correlation for the template author. The practitioner does not reconcile logs by hand; the unique-address-per-payload design does it.
The same callback machinery confirms several unrelated bugs, which is why it is worth building once.
Not all callbacks are equal, and conflating them is a common reporting error.
An HTTP interaction from the target is strong: something performed a full request to a unique URL that existed only inside one payload. A DNS-only interaction is weaker, for three reasons.
First, DNS resolution is recursive and indirect. The query that reaches your authoritative server usually comes from a resolver, not from the vulnerable host, so the source address of a DNS interaction is often the organisation's resolver or a cloud DNS service, not the machine that ran the payload. That is still evidence the name was resolved somewhere in the target's path, but it does not place the lookup on the vulnerable host.
Second, egress policy commonly allows DNS while blocking outbound HTTP. PortSwigger documents the exact pattern: it is common to see a DNS lookup for the callback domain but no subsequent HTTP request, because the application tried to make the request, which triggered the resolution, but the connection itself was blocked at the network layer. A DNS-only hit is consistent both with a real vulnerability behind an egress firewall and with something merely resolving a name it saw.
Third, names get resolved for reasons that have nothing to do with code execution, which is the subject of the next section. An HTTP interaction is much harder to produce by accident than a DNS lookup.
So a DNS-only callback should be reported as a weaker observation than an HTTP callback, and where it matters, the payload should try to force a full HTTP interaction to raise confidence before the finding is called confirmed.
The out-of-band channel has its own failure mode: something other than the vulnerability contacts the callback host. These do not look like ordinary false positives, and triaging them needs different signals.
Three signals separate these from a real finding, and they are general triage practice rather than any one product's method:
The converse error is reading an absent callback as "not vulnerable". An out-of-band check only produces a negative you can trust when the target could actually have reached the callback host.
An out-of-band negative is therefore conditional. It is only meaningful alongside evidence that the callback path was open, which is one more reason a scan's coverage record matters as much as its findings.
Where the callback server lives is a data-handling decision. A public, shared interaction server is the zero-setup option, and it is the right one for a quick check. But the payloads, and anything the target concatenates into a callback, leave to a third party. For blind SQL injection the whole point can be to exfiltrate data within the DNS name, so a confirmed finding on a public server may have sent a fragment of production data through someone else's infrastructure. Interactsh encrypts interactions before handing them back to the client, but the server sees them in the clear on arrival, and the default public servers are documented as subject to rotation or downtime at any time, so they are explicitly not something to depend on. Self-hosting keeps the data in your own boundary, at the cost of running it: you need a domain whose nameservers are delegated to your server, the matching NS and address records, a reachable host, and a wildcard certificate if you want HTTPS interactions. Burp Collaborator's documentation describes the same private-server model and notes that using a bare IP instead of a delegated domain disables DNS-based detection and the interactions that depend on it.
Callbacks define scope, and scope must be enforced. An out-of-band payload is an instruction to make a third party, or the target, contact your server. Pointed at a host you are not authorised to test, that is unsolicited traffic you induced, and a callback hostname embedded in a payload can travel further than the system under test, into logs, mail and downstream services. The scope boundary for out-of-band checks is the same discipline argued for active external checks in why an EASM finding needs evidence: the hosts you probe, and the systems you induce to call home, should be inside an authorised scope that is explicit and enforced, not inferred.
AppSecD's dynamic testing uses Nuclei, Dalfox, SQLMap, Commix, Katana, Arjun, FFuf and Interactsh for out-of-band detection, alongside more than 150 native checks and API discovery across more than 50 web frameworks. It uses Interactsh as the out-of-band channel, and those findings go into the same deduplicated finding model and lifecycle as the rest of the platform. The mechanism, triage signals and checklist in this post, correlation per parameter, the source network of an interaction, the timing window, and treating a DNS-only hit as weaker than an HTTP one, are general out-of-band practice, not a description of how AppSecD generates payloads, correlates interactions or weighs a callback internally.
Whatever tooling produced the callback, these questions separate a finding from an artefact.
A blind vulnerability is defined by the response telling you nothing. The callback is the only thing that does, so the discipline is in reading it precisely: which channel, from where, tied to what, and when.