BlackHat mea Quals 2025 — Web challenges
I recently participated in Blackhat mea ctf 2025 quals. These are the write-ups of the Web challenges that I solved. I hope you will learn something new.
Happy Hacking!!
koko WAF
we were given the challenge files and the source code for the whole web application to analyze
1 | └─$ tree kokowaf-player |
looking at waf.php
1 |
|
The WAF filters out specific SQL keywords and characters using preg_match
function,
The regex translates to :
- No quotes ‘ or “
- No logical operators (or, and)
- No union, select, from
- No comments (/**/)
- No spaces
Now looking at the relevant section from index.php
:
1 | $username= $_POST['username']; |
Key points:
- Passwords are stored as SHA1 hashes.
- Only the username field passes through the WAF check.
- SQL query is vulnerable because of string concatenation:
1 | select * from users where username='$username' and password='$password' |
If we somehow managed to bypass the WAF filter, we could inject into the $username
field.
We spent nearly eight hours straight throwing every trick in the SQLi playbook at this filter — case toggling, encodings, comment injections, whitespace bypasses — you name it, we tried it. Nothing worked. This WAF laughed at every payload we had up our sleeves… until we realized the real weakness wasn’t in what it blocked, but in how it was getting blocked.
While digging around, my friend came across a similar challenge writeup from last year. That was the lightbulb moment.
The key insight is that regex engines aren’t parsers — they have their own quirks:
- Greedy → they try to consume as much input as possible.
- Vulnerable to catastrophic backtracking → overly long or repetitive inputs can overwhelm them, causing the pattern matching to fail or behave inconsistently.
So instead of trying to sneak around the regex rules, the real solution was to break the regex itself.
We constructed a massive prefix before our injection payload:
1 | prefix = 383838 * '"' |
This effectively overloaded the regex engine. By the time it scanned through hundreds of thousands of characters, it failed to correctly detect the final malicious injection at the end of the string and provided a false (thus bypass the check in the code above in waf.php
).
after that we craft our blid bool-based SQLi injection payload and append it to the exploit and it will work
1 | ' or (if((select(flag)from(flags)) like ('BHFlagY{a%'),1,2)=1)# |
then we coded a script that retrive the flag character by character and checks everytime if it starts with the “privously” check flag and append to it.
so our final exploit was :
1 | import requests |
- Title: BlackHat mea Quals 2025 — Web challenges
- Author: Depe
- Created at : 2025-09-10 01:07:12
- Updated at : 2025-09-10 02:26:50
- Link: https://depe.blog/2025/09/10/BlackHat-mea-Quals-2025-—-Web-challenges/
- License: This work is licensed under CC BY-NC-SA 4.0.