Exploiting Access-Control-Allow-Allow-Origin: *
Vulnerable application for practice: https://github.com/tauh33dkhan/cors-exploit-with-cache
When searching for CORS misconfigurations, you might have come across applications that return the response header Access-Control-Allow-Origin: *
. Normally, this is not exploitable due to the browser’s Same-Origin Policy (SOP), which prevents the reading of responses from cross-origin requests if this response header is present and the JavaScript code has sent the request with cookies. In such cases, the browser returns the following error message.
But what if we send a request without a cookie?
In that case, you will be able to read the response, but it will be an unauthenticated response, which is of no use.
Now, let’s delve into other aspects. The endpoint I was targeting also had browser caching enabled.
Here is how browser caching works:
When a user requests a resource from server first time, the server generates a unique identifier for that specific version of the resource. This identifier is the ETag. The ETag is then sent to the client (browser) along with the requested resource. The browser stores the received ETag. When the client wants to request the same resource again, it includes the stored ETag in the request headers. Upon receiving a subsequent request, the server checks if the ETag sent by the client matches the current ETag of the resource. If the ETags match, the server can respond with a “304 Not Modified” status, indicating that the resource has not changed since the client’s last request. If the ETags do not match, the server sends the updated resource along with a new ETag.
the endpoint which I was targeting also had browser caching enabled which can be confirmed by the following response
So what?
Well, this means that the authenticated response is cached in the browser, and we need to find a way to read this response without sending a request to the server.
What? Without sending a request to the server?
Yes, we can use the fetch request option ‘cache: force-cache’ to read the cached response from the browser without sending a request to the server. Additionally, we will also not include the cookie when making the request to bypass the Same-Origin Policy (SOP) restriction, as the authenticated response is already cached in the user’s browser.
However, I observed that the browser has now stopped loading the cached content from the local disk without sending a request to the server if the request is not originated from the same site. This means that I will have to look for an XSS vulnerability in one of the subdomains to exploit it. Luckily, I also found an XSS on a non-sensitive subdomain, which I can use now to retrieve the sensitive information.
Exploit:
<script>
async function exploit(){
var url = "http://tauheedkhan.com/userdata";
var response = await fetch(url, { method: 'GET', cache: 'force-cache'})
var data = await response.json()
alert(data.data)
}
exploit()
</script>
Attack flow:
Attacker inject the above code on XSS vulnerable subdomain -> user visits the vulnerable subdomain -> XSS triggers the request to sensitive endpoint to steal the sensitive data from local cache.
Reference:
Original Issue Reported In Chrome: https://bugs.chromium.org/p/chromium/issues/detail?id=988319#c11
Hackerone report: https://hackerone.com/reports/761726
Read more about chrome cache: https://developer.chrome.com/en/blog/http-cache-partitioning/
Chrome marked it as won’t fix but they have added some logic in cache key that is why we are only able to exploit it from samesite.
Vulnerable browser: I have confirmed this vulnerability on Chromium based browsers.