This write-up is short and focused โ I used a little AI help just to clean up grammar and flow ๐. Here's what happened and exactly how I bypassed Cloudflare's WAF to trigger an XSS.
One afternoon in my fourth year of college I skipped a lecture. Some days you just wake up with the urge to hunt for bugs, and that day was one of them. I opened HackerOne, picked a newly launched VDP program (I'll call it redacted.com), and let my recon script run while I started poking around the app with Burp. After clicking through a bunch of pages, I found a simple search field โ the kind that makes you think "maybe there's something here."
I tried the usual stuff first. SQLi probes didn't show anything, so I moved on to XSS. I dropped in <img src=x onerror=alert(1)>
and <script>alert(1)</script>
, but Cloudflare blocked both. Most people would probably move on after that, but I've read enough writeups to know that "blocked" doesn't always mean "blocked for good." So I decided to test what exactly the WAF was filtering.

I started small. I sent only special characters like <
, >
, "
, /
โ and those passed. Then I tried tokens: <script>
was blocked, alert
was blocked, and onerror
in lowercase was blocked. But the <img>
tag was also blocked. Browsers don't care about case for tag names or event attributes, but some filters do. If the WAF was checking for exact lowercase words, maybe a mixed-case version would slip through even though the browser would still run it.
I tried mixed-case and mutated versions. OnErroR
passed while onerror
didn't. AlEt
still got blocked, but confirm
wasn't filtered. Putting these together, I made a simple payload that worked:
<Img Src=OnXSS OnErroR=confirm(document.cookie)>
Because HTML is case-insensitive, the browser treated Img
and OnErroR
the same as img
and onerror
. Cloudflare's filter, however, missed the mixed-case tokens and the confirm()
call went through. The XSS executed and the cookie showed up in the confirm dialog. It was small and silly โ but it worked.

I reported the bug to the platform as a VDP submission. They validated it in two days. No money โ it was a VDP โ but getting that validation felt great. This was my first confirmed XSS and it only took about an hour of hunting. That's the strange mix of luck and effort in bug bounties: sometimes you grind for days and find nothing, and sometimes a tiny idea pays off fast.
What I learned is simple: filters that just look for exact words or don't clean input properly are easy to fool. If you're building something, make sure you convert input to a common form before checking it, use real HTML/JS parsing instead of crude pattern matching, block risky attributes on the server side, and add a strict Content Security Policy. If you're hunting, try small mutations, test different JS functions, and don't assume "blocked" means "done."
Thanks for reading โ it means a lot if you made it this far. Keep breaking things, keep fixing them, and never stop learning. I'll write next blog soon about how I bypassed a premium restriction to download files that should have been behind paywalls by really simple method.
Follow me on twitter/x
Connect me on linkedIn