February 28, 2022

The #15th DOJO CHALLENGE consisted in bypassing different security levels simulating the behavior of a WAF in order to execute JavaScript code in an arbitrary way. This DOJO was created by one of our community members. Thanks to Brumens for this great challenge!

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


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


  • The best writeups reports were submitted by: iQimpz, ctfpoulpe and w31rd0

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

Tell me a WAF that never been bypassed…?

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.


iQimpz‘s report was well detailed and really useful to understand the logic and to get around the different filters. All the steps are clearly explained and it is very helpful for everyone to understand how they went about solving this DOJO.

The others reports, notably ivarsvids‘s and ctfpoulpe‘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. Also congrats Remmer for the 1st blood!

Thank you all for playing with us!

iQimpz‘s Write-Up

————– START OF iQimpz REPORT ——————


For DOJO Challenge #15, we are tasked with setting settings.user to Admin, and alerting or logging that value to the console.

Our injection point is inside of the alt attribute of a <img> element. Our input is filtered by Brumens “Firewall” before being injected. This “Firewall” filters many characters and keywords, but as we have seen so many times, it can be bypassed.


Source Code for the Challenge, with our filtered input being placed at $inject:

<h1><span style='color:red;'>/</span> The Amazing Blog</h1>
  Just sharing a cool photo at my new desktop at the YesWeHack office!
  <img style="width:340px;height:160px" src="" alt="$inject">

   // Also did you knew that HTML execute <script> tags first and then execute all other onload statments after? ;)

  var settings = {
    user: ""
  settings.user = "Guest";
  Firewall = "SuperDuperSecureMode"; // - DON'T TOUCH THAT FIREWALL CONFIG GUYS, GOT DAMN IT!


<!--[Uncomment to verify]-->
<!--<body onload="alert('Result: ' + settings.user)"></body>  -->

| Design below it's not part of the challange. (No need to read this source code) |
  body {
    padding: 15px;
    background-color: #000;
    border-right: 1px solid red;
    border-bottom: 1px solid red;
  h1 {color:#fff;}

From the source code alone, this challenge seems pretty straight forward. Just get some javascript execution, then set settings.user = 'Admin';

Now let’s analyze the WAF:

The filters highlighted in yellow above the “URL Decode” filter, can simply be bypassed using URL encoded characters. The characters below the “URL Decode” filter can possibly be bypassed using hex or unicode characters, depending on where exactly we are trying to replace those characters.

Forming our payload

Here I will talk about the development of the payload and show how it grows progressively.

Looking at the WAF, we will not be able to inject new HTML tags, but that is fine because we have event handlers still. First, we will need to get out of the alt attribute. We can simple do this by adding a " or %22 because it will be replaced with ", but that will not escape the quote in this case.

Payload: %22

Now to inject our event handler, we will need a space, but all spaces are not allowed by the s filter. Luckily we can use a forward slash / and space interchangeably in HTML. The / is not allowed, but since it is not URL decoded first, we can use %2f.

Payload: %22%2ftest

Now, naturally, we need a event handler to execute our javascript. We will use onload. We can see that the WAF filters event handlers with this filter: on.*=, but since our payload isn’t decoded before checking for this, we can again just use a URL encoded character to bypass this.

Payload: %22%2f%6fnload=test

Next, we need to set settings.user to Admin. We cannot access the user property of settings this way: settings.user because the . is filtered. But there is another way to access properties of an object like this settings['user']. But here we come across another problem. ' and " are replaced with ' and " respectively. Quotes worked to escape the alt attribute, but will not work here. But they left out the backtick within the WAF, which we can replace the quotes with backticks. We can also use the backticks for the Admin string as well.

Also, now that we are executing javascript, we need to make sure that it doesn’t produce an error with the " that is appended to our payload because of our injection point being inside of an attribute. We can simply add %2f%2f to the end of our payload, which is converted to //, in order to comment out the appended "

Payload: %22%2f%6fnload=settings[`user`]=`Admin`;%2f%2f

After executing this, we can see that we have now set settings.user to Admin! If you are checking in the console, make sure you are executing the javascript within the iframe for the challenge.

Our next task is to pop an alert with that value. Let’s start with just a simple alert(1). From the WAF, we can see that we cannot simply use alert, but we can convert on of the character values to unicode as a bypass alu0065rt. But again, the WAF will escape the backslash like: alu0065rt. But we can just URL encode the backslash to get past this: al%5Cu0065rt. But the WAF blocks ( and ), so we cannot just do: al%5Cu0065rt(1). But we can replace the parentheses with backticks!

Payload: %22%2f%6fnload=settings[`user`]=`Admin`;al%5Cu0065rt`1`%2f%2f

This alert with backticks will only work with strings though, and we need to alert some form of settings.user. One way to still use backticks instead of parentheses and access variables, is to use setInterval. And we can hex encode special characters inside of the backticks for them to be evaluated: setInterval`alertx28settings.userx29`;. This will work in the console, but there are still problems we have to bypass. We already know that we can use settings[`user`]=`Admin`; instead of settings.user. But if we put that inside of the setInterval, the nested backticks will cause a problem. So we can just set a variable equal to that.

Payload: %22%2f%6fnload=settings[`user`]=`Admin`;temp=settings[`user`];%2f%2f

Now that we don’t have to worry about nested backticks, our setInterval looks like setInterval`alertx28tempx29`;. We know we have to replace alert with al%5Cu0065rt. The last thing that is causing a problem is the for the hex encoded parentheses. For this, we can simply URL encode the backslashes. Then we have our final payload!

Final Payload


————– END OF iQimpz REPORT ——————