Double URL-encoded XSS

Right after the quarantine ended I had a mini burn out for more than a month, where I didn’t even bother to try and hunt for bugs. After those one and a half months of trying to find my motivation again, I found a XSS that required a bit more thinking than the typical javascript:alert(1) cases.

Revisiting my old notes from a BB program, I observed an endpoint that I tried to exploit the last time, before I go through that burn out. Back then I wasn’t able to exploit it, so now I thought I’d give it a second chance. The endpoint was https://example.com/dir/legacy-browser?redir=/. There was a button, where you would get redirected to the endpoint the paramteter redir was pointing. Analyzing the source code, I could see that the website was redirecting the user this way:

At first, I thought that this should be easy. I tried escaping the parenthesis by injecting ') in the redir parameter. The result was that the illegal characters would be removed. Actually, if there was any illegal characters, the app would redirect you by default to /.

I tried to inject a null byte %00, just to see how the app would react. The app translated the null byte into NULL. So the payload redir=/%00/ would get reflected into the source code as javascript:redirect('/NULL/'). I thought “great, the url encoded characters are getting decoded”. I clicked the button and… I got redirected to /NULL/. Also, any other illegal url encoded characters would be removed, just as I described previously.

So, apparently, the URL was getting decoded and sent to javascript:redirect(). But I wanted to inject some illegal characters to escape the parenthesis. After some thought, I came to the conclusion that in orded to send illegal characters to javascript:redirect(), those characters had to be URL-encoded, after they have been decoded from the URL bar. Hence the double URL encoding!

I sent the second payload https://www.example.com/dir/legacy-browser?redir=/%2500/test, where %2500 is the double-URL-encoded null byte. The result was for javascript:redirect('/%00/test') to get reflected. After hitting the button, I got redirected to https://example.com/, meaning that the null byte in javascript:redirect() was actually getting decoded. So I could actually send working double URL encoded payloads.

Having this in my mind, I crafted a payload for this endpoint (and by crafting, I mean copying and pasting the XSS payload from brutelogic in PayloadsAllTheThings and adding a ') in front). I double URL-encoded it and injected it into the parameter. And after hitting the button, the XSS was getting triggered, right before the user got redirected to /.

Final exploit: https://example.com/enpoint?redir=/%2527)%253B%2520alert(document.cookies)%253B%252F%252F

tl;dr

10 Likes

Dude this is awesome!!!

I’m so happy you got this one too! Good job!

2 Likes

Thanks man!! :smile:

1 Like