$ cat writeup.md…
$ cat writeup.md…
broncoctf2026
Task: Flask web page hides a flag inside a CSS-blurred image, gated behind solving 500 calculus problems tracked only in client-side JavaScript. Solution: read the page source to find the unauthenticated /api/v1/internal/fetch-config-blob endpoint that serves the original unblurred PNG on page load, then curl it directly to read the flag from the image.
My friend tried to motivate me to review my derivatives by telling that me that I can unlock a top-secret image after I solve 500 challenges on this website. Unfortunately for her, I'm a firm believer in work smarter not harder, so I wonder if there's a way I can get the flag without actually doing any math?
A Flask web app ("Calculus Review") shows a blurred flag image and asks you to solve 500 derivative problems to "unblur" it. The goal is to read the flag without solving 500 math problems. The description's "work smarter not harder" is a direct hint that the gate is fake.
Recon of https://broncoctf-unblur-me.chals.io/:
Server: Werkzeug/3.1.8 Python/3.14.6 (Flask)
The single HTML page contains all the logic inline. Three things stand out:
The blur is pure CSS, not a server-side render:
#flag-image { filter: blur(20px); ... }
Removing this one CSS property reveals the original image.
The 500-problem gate lives entirely in client-side JavaScript — checkAnswer() increments a local correctCount and only calls flag.style.filter = "none" when correctCount >= 500. There is no server-side verification of progress.
The secret image is fetched immediately on page load, before any question is answered, from an internal API endpoint:
function loadSecretImage() { fetch('/api/v1/internal/fetch-config-blob') .then(r => r.blob()) .then(blob => { img.src = URL.createObjectURL(blob); }); } loadSecretImage(); // runs at load time
The endpoint /api/v1/internal/fetch-config-blob is unauthenticated and returns the original (unblurred) PNG regardless of progress. The only thing the browser does afterwards is optionally blur it via CSS. So the entire access control is client-side theater — direct object access defeats it.
Step 1 — grab the page and read the source:
curl -sk https://broncoctf-unblur-me.chals.io/
This reveals the internal endpoint /api/v1/internal/fetch-config-blob in the inline <script>.
Step 2 — hit the endpoint directly to download the unblurred image:
curl -sk -o secret.png https://broncoctf-unblur-me.chals.io/api/v1/internal/fetch-config-blob
The response is application/octet-stream, ~325477 bytes, a valid PNG.
Step 3 — open secret.png. The flag is rendered as text inside the image:
BRONCO{REDACTED}
Normalize the wrapper to the required lowercase bronco{...} format.
(Alternative, fully in the browser: open DevTools and either delete the filter: blur(20px) CSS rule on #flag-image, or paste document.getElementById('flag-image').style.filter='none' in the console — no math required.)
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ grep --similar