Unblur Me
Unblur Me
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: client_side_gate_bypass, css_blur_bypass, direct_endpoint_access, page_source_analysis
Summary
Walkthrough of the CUSTOM Web Unblur Me. 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 unauthentica
title: "Unblur Me" id: 20260711_broncoctf2026_unblur_me category: web difficulty: easy event: broncoctf2026 tags: ["flask", "source_code_analysis", "werkzeug", "web", "client_side_access_control", "direct_object_access", "css_blur_bypass", "hidden_api_endpoint", "image_flag"] techniques: ["client_side_gate_bypass", "css_blur_bypass", "direct_endpoint_access", "page_source_analysis"] tools: ["curl", "browser_devtools"] indicators: ["blurred flag image gated by client-side JS counter", "filter: blur(20px) applied via CSS", "fetch() loads secret resource on page load before any check", "internal API path /api/v1/internal/fetch-config-blob in source", "Werkzeug/Flask server", "\"work smarter not harder\" hint in description"]
Unblur Me — broncoctf2026
Description
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.
Analysis
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:
css read-only • copy allowed#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 localcorrectCountand only callsflag.style.filter = "none"whencorrectCount >= 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:
js read-only • copy allowedfunction 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.
Solution
Step 1 — grab the page and read the source:
bash read-only • copy allowedcurl -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:
bash read-only • copy allowedcurl -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.)
Key Takeaways / Lessons
- Mastering client_side_gate_bypass, css_blur_bypass, direct_endpoint_access, page_source_analysis was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-07-11 • CUSTOM • Unblur Me • Difficulty: Easy