DOJO CHALLENGE #8 Winners!

March 29, 2021

The #8th DOJO CHALLENGE was on finding a way to bypass 3 security mechanisms to prevent a SSRF and to retrieve a secret flag.

WINNERS!

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

3 BEST WRITE-UP REPORTS

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

EvilCorp2.0 has a script to check the current state of an internal service. They want to make sure that this script, with all its security, cannot be used to retrieve the secret path…Find a way to bypass all security mechanisms to retrieve the /secret.

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

Holme‘sreport was detailed, informative, and good at explaining the logic to bypass every security mechanisms in script to finally obtain the secret flag hidden in /secret.

The others reports, notably neolex‘s and Pinky‘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!

Holme‘s Write-Up


————– START OF Holme REPORT ——————

Write-up

For the latest addition to the Dojo series, we’re faced with the challenge of fetching the secret that EvilCorp2.0 is storing on the /secret endpoint. To obtain this secret, we need to find a way through the security mechanisms that EvilCorp2.0 has put in place for their small app. Their app is created using Deno (https://deno.land/) and allows us to specify a URL as a string, which the server will fetch for us if it passes the security mechanisms.

The security mechanisms are implemented as a function called waf. The function takes a parameter of type ‘string’ called str. The function starts by creating a URL object named url by using the user-provided string:

The rest of the function implements 5 security mechanisms which do the following:

  1. Skips all security checks if str (not the URL object) is equal to http://127.0.0.1:5000/ping

2. Makes sure that the port from url.port is 5000

3. Makes sure that url.hostname is not 127.0.0.1 or localhost

4. Makes sure that url.pathname starts with /ping

5. Makes sure that str doesn’t not contain the substring /ping<ANY_CHARS>/.
The following would eg. not pass: http://example.com/smth/pingblabla/hihi

Time to exploit

We now have a basic understanding of the app, so let’s exploit it! Let’s start by providing the string http://127.0.0.1:5000/ping which we now will pass the security mechanisms because of check 1 in waf

Cool, we got a ‘pong’ from the /ping endpoint. Let’s now bypass some of the security mechanisms! There doesn’t seem to be a reason we would want to bypass the ‘port’ check since we assume that the /secret endpoint exists on port 5000, so let’s move on to check 3. Hmm, this seems to be a pretty insufficient way to avoid requests to localhost. While we can’t use 127.0.0.1 or localhost, there's plenty of other options out there that will resolve to localhost. Simply '0' would often do! Let's try using the payloadhttp://0:5000/ping` to see if we can bypass this check and receive a ‘pong’:

Yup, it worked! Nice, let’s move on then. Check 4 ensures that the pathname property of the url object starts with the value /ping. This seems pretty hard to work around, but there’s one important thing to notice in the app’s logic! Notice that the url object only exists within the scope of the waf function, and that it’s actually the string, which is also named url (confusing, huh?) that’s provided as a parameter to fetch after the waf function is passed.

So why care about this? Well, wouldn’t it be nice if there was some inconsistency between the URL object in Deno and the user-provided string! What if eg. the pathname property of the URL object didn’t match the actual path being fetched when sending a request to the user-provided string? In other words: Can we craft a payload, which when used to create a URL object, causes some unexpected behavior?

Try, fail, try again

Let’s create a test environment to test for inconsistencies in the Deno URL object. To do so, we can simply use the Dojo Playground!

We now have a simple app that logs the value of url.pathname for us. Time to test! Maybe some simple path traversal trick will do the job?

Hmm, sadly no. But maybe we could try and do some URL-encoding? But first, a quick example. Visting a URL like: https://example.com/ping/%2e%2e%2fhihi from most browsers would result in the following:

As we can see, we didn’t end up on /hihi but /ping/..%2fhihi instead. But the notice how the period chars were decoded… What would happen if we instead visited https://example.com/ping/%2e%2e/hihi? You guessed it:

The path was resolved to /hihi. Let’s try to apply this knowledge to our Deno test set-up and use the payload: http://0:5000/ping/%2e%2e/secret:

Would you look at that! Deno didn’t seem to care about our URL encoded period chars, and tells us that the pathname is simply /ping/%2e%2e/secret. But what will happen when we fetch this payload..?

Let’s head back to the challenge and find out!

Oh, we forgot about the last check… We can’t use any / chars after the string /ping. But could we maybe just use something else than / ? Well, how about some nice \ ! Let’s try with the payload http://0:5000/ping\%2e%2e\secret:

Voilà!🎉 We solved the challenge and retrieved the secret: flag{You just found a secret !}

Thanks for reading along!
Happy hacking,
holme

————– END OF Holme REPORT ——————