DOJO CHALLENGE #10 Winners!

May 25, 2021

The #10th DOJO CHALLENGE was to build a JavaScript payload in palindrome. We received many unexpected solutions on this DOJO with a workaround in just a few characters!

WINNERS!

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

The FIRST and 4 best quality write-up reports:

  • The fastest hunter : Ivarsvids
  • The 4 most beautiful reports: Ctfpoulpe, Pbeaune, Blaklis, Rekter0

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

A palindrome is a word, number, phrase, or other sequence of characters which reads the same backward as forward, such as madam or racecar. There are also numeric palindromes, including date/time stamps using short digits 11/11/11 11:11 and long digits 02/02/2020. Sentence-length palindromes ignore capitalization, punctuation, and word boundaries.

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

Pbeaune’s report was detailed, informative, and good at explaining the logic to construct the magic payload to obtain the flag.

The others reports, notably Ctfpoulpe’s, Blaklis’s and Rekter0’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!

Pbeaune’s Write-Up


————– START OF Pbeaune REPORT ——————

Description

The goal is to find the flag in the DOJO Playground with abusing input and control validation using $payload parameter:

Exploitation

Here is the source code of the challenge :

function random(){
  return Math.random().toString(16).slice(2)
}

const KEY = random()

function reverse(s){
    return Array.from(s).reduce((acc, c) => c + acc, "")
}

function mirror_eval(s){
    try {
        return eval(s + reverse(s))
    } catch (e){
        console.log(e)
    }
    return null
}

function check(s){
  const FLAG = `FLAG{${random()}}`
  if (mirror_eval(s) == KEY){
    console.log(FLAG)
  }
}

const payload = ""

check(payload)

We have one input:

$payload

and some characters are filtered : “;” “//” “–” “/”

The input is passed to mirror_eval() function before test.

if (mirror_eval(s) == KEY){
    console.log(FLAG)
}

A part of mirror_eval(s) function build an new string : s + reverse(s)
ex : mirror_eval(“123”) return “123321”

We can see the eval() function is used in mirror_eval() function.

return eval(s + reverse(s))

This native javascript function execute the string expression passed as argument(See more details).
ex: eval(‘1+1’) return 2

So we could easily build an expression with comment and just evaluate KEY
ex: eval(“KEY//”) return value of KEY.

but “/” is filtered!

We can use a boolean expression : foo || bar return foo (if first condition is true the second is not evaluated).
It’s work even if bar is undefined

eval(“foo || bar”) return value of foo

Now we have to deals with mirroring code : s + reverse(s)

With the following payload : KEY| , s + reverse(s) creates string “KEY||YEK” and eval(“KEY||YEK”) return value of KEY.

Thus, we pass the conditional test and get the flag.
FLAG{9f447dd57affb}

PoC

$payload=KEY|

Risk

By pass conditional test (as authentication,user rights…)

Remediation

It’s not recommended to use the eval() function.
So remove eval() function and replace line by : return s+ reverse(s)

Another way is to filter more characters as “|” or add more checks as length of input, hexadecimal characters…

————– END OF pbeaune REPORT ——————