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?
1
?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:
1
2
3
4
?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 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:
1
2
- 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:
1
?msg=<img/src=`%00`%20onerror=this.onerror=confirm(1)
This was my next payload, it got reflected but no XSS unfortunately.
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.
1
<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.
1
<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.
1
<svg/onload=eval(atob('YWxlcnQoZG9jdW1lbnQuY29va2llKQ=='))>
The following encoded 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!