BlackHat mea Quals 2025 — Web challenges
I recently participated in Blackhat mea ctf 2025 quals. These are the write-ups of the Web challenges + File 101 pwn challenge that I solved. I hope you will learn something new.
Happy Hacking!!
File 101
We were provided with a small set of files to poke at:
1 | . |
main.c
1 |
|
What the Code Is Actually Doing
At first glance, the program looks too small to hide anything interesting — but that tiny scanf call is doing something unbelievably reckless.
After turning off buffering for stdin/out, the binary does:
1 | scanf("%224s", (char*)stdout); |
It literally treats the internal stdout and stderr objects as writable buffers and lets the user shove 224 bytes into them.
These aren’t normal buffers — they’re FILE structures used by libc to track stream state: vtable pointers, flags, buffer positions, mode bits, everything. Blindly overwriting them means we basically get to hijack libc’s file-handling machinery.
This is exactly what FSOP (File Stream Oriented Programming) is about: you poison an _IO_FILE object so that the next time libc interacts with it (flush, close, write…), your carefully staged fake structure redirects to shellcode.
Theory
- smash stdout to force a libc pointer leak
- use the leak to compute the actual libc base
- corrupt stderr into a forged FILE object where the vtable points to something juicy
- make libc “use” that FILE, and execution jumps into system(“/bin/sh”)
exploit
1 | from pwn import * |
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-12-06 13:22:43
- Link: https://depe.blog/BlackHat-mea-Quals-2025-—-Web-challenges/
- License: This work is licensed under CC BY-NC-SA 4.0.