Bypassing WAF to perform XSS

Kleitonx00
May 28 · 4 min read

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.

Image for post
Image for postImage for post
Admin Panel on website.com/admin

Instinctively I tried entering random credentials to see what kind of response I will get.

Image for post
Image for postImage for post
/admin/index.php?msg=Invalid%20Email%20and%20Password

/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

Image for post
Image for postImage for post
?msg=Hello World was reflected

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>

Image for post
Image for postImage for post
HTML Injection

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&colon;confirm&lpar;1&rpar;”
?msg=<iframe %00 src=”&Tab;javascript:prompt(1)&Tab;”%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:

Image for post
Image for postImage for post
Malicious XSS requests blocked by WAF

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 :(

Image for post
Image for postImage for post
<img bypassed 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)

Image for post
Image for postImage for post
svg injected but no XSS popup

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!!!

Image for post
Image for postImage for post
Finally 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.

Image for post
Image for postImage for post

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.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade