I dive into a multi-layered OSINT (Open-Source Intelligence) investigation that proves the "old days" of manual, high-dopamine digital sleuthing are far from over in this AI dominated time.
Unlike my usual deep dives, this blog post will contain my thoughts while solving bits CTF 2026 web challenge rusty-proxy which was victim to agentic AI, but insightful nonetheless.
This challenge demonstrated that security vulnerabilities often exist not in the code itself, but in the glue connecting different components. While the PHP application and the `expect` script appeared logically sound in isolation, the vulnerability emerged from the behavior of the Linux TTY subsystem.
I explore a misaligned trust chain between a CDN, a Tornado web app, and an admin bot that allows cache poisoning via a GET request body. This lets us serve an XSS payload to the admin. We then abuse environment variables injection to get RCE
I exploit Python's introspection and format string tricks to escape a jail that bans letters, digits, and every binary operator except modulo.
I abuse window.opener to navigate the admin's tab to /get_flag and exfiltrate the response, bypassing CSRF protection entirely.
I pollute Object.prototype through a custom query parser to smuggle an onload attribute past sanitize-html's attribute whitelist.
I exploit a search endpoint that redacts but still matches the flag, using a 3-character sliding window oracle to brute-force it character by character.
I smuggle HTTP/2 cleartext requests past NGINX to reach a restricted endpoint, then use shell expansion to read the flag under a strict character filter.
I bypass a Jinja2 sanitizer by splitting a payload across two form fields, then chain Flask's request object to reach subprocess and execute commands.
I simulate iterative knowledge deduction between two players to find the minimum number of nights before one can declare the true total.
I exploit the gap between Express header handling and Node's body 'end' event to register a self-referring account and compound referrals into 100 billion coins.
I send number[]=1000000000 to trick Express's qs parser into producing an array that passes a length check and coerces to the target number.
I level up a dummy account by abusing the battle endpoint, then trilaterate the target's coordinates from three distance measurements to find their exact address.
I abuse a URL substitution flaw to reach an exposed dockerd TCP socket, then create a container with the host filesystem mounted to read the flag.
I exploit a misconfigured Nginx alias to read the jwt.secret file, then forge an admin JWT token to access the hidden post containing the flag.
I send concurrent requests to exploit a non-atomic post count check, exceeding the 12-post threshold needed to unlock the flag endpoint.
I override the validation server via a debug cookie, serve a future-dated signed timestamp from my own server, and chain the unlocked feature into RCE to read the flag.
I race a symlink swap between a large file and /proc/pid/environ to sneak past the os.stat zero-size check and leak the flag from the environment.
I inject a reflection-based payload into an unsanitized Dynamic LINQ Where() call to execute arbitrary commands and read the flag from the filesystem.
I bypass a Nginx exact-match rule via PHP-FPM path confusion to expose phpinfo(), then inject a base64-encoded XSS payload using a form feed character as whitespace to exfiltrate the flag.
I bypass dot-blocking by URL-encoding path separators, then use /proc/self/cwd to resolve the working directory and read the flag file directly.
I extract the hardcoded secret key from the source code, re-sign a token with admin set to true, and replace the cookie to pass the signature verification check.
I modify the admin field in the JWT payload to true, re-sign it with an arbitrary key, and access the flag since the server performs no signature verification.
I curl the page to avoid an infinite loop, then run the script in Node.js to resolve the %c format specifier and reveal the actual flag string.
I archive a file named with a Jinja2 payload, exploit Flask's unsanitized render_template_string call, and chain the request object to import os and execute arbitrary commands.
I share how I got into CTFs, which tools and categories to start with, and why practicing through challenges beats passive learning every time.