SINDEL CTF — IDOR Masterclass Writeup

SINDEL CTF — IDOR Masterclass Writeup

18/18 flags captured across 11 IDOR categories on the SINDEL ("Shadow Intelligence Network") lab. Engagement performed black-box, starting with zero credentials.

All flags captured


Target

SINDEL — a fictional spy-agency web application running on localhost:8888. The lab is purpose-built to teach Insecure Direct Object Reference (IDOR) vulnerabilities and contains 18 distinct flags spread across 11 vulnerability classes:


TL;DR — The Story

  1. Recon revealed that unauthenticated pages leak the entire IDOR cookbook in client-side HTML and JavaScript.
  2. Pre-auth bypass via a sequential magic-link endpoint leaked every operative's full PII without credentials.
  3. Foothold as the default training account — bio literally said "Standard entry point" — password guessed via regional naming convention (Yaw@2025).
  4. Systematic IDOR sweep through every identifier type the API exposed. Every endpoint authenticated the user but failed to authorize the request.
  5. 4-digit verification code brute-forced in seconds — no rate limiting.
  6. Account takeover via forgeable password-reset tokens, enabling a Recruit (ALPHA clearance) to hijack a Senior Operative (OMEGA clearance) and exfiltrate a TOP SECRET mission.

Root cause across all 18 findings: the application checks "are you logged in?" but never "are you allowed to see this?"


Phase 1 — Reconnaissance

Before touching authenticated endpoints, I mapped what the app reveals to unauthenticated visitors. Every single HTML page in the application ships its full source and JavaScript before any login check fires — the redirect happens client-side.

The page /tokens.html is especially generous. It literally contains JavaScript that computes the exploit primitives:

const magic  = `magic-${String(id).padStart(6,'0')}-sindel`;
const reset  = btoa(`reset:${id}:sindel`);
const qr     = `QR-${String(id*1337).padStart(8,'0')}`;

/access.html documents the base64, hex, and URL encoding patterns. /verify.html tells the user that the verification code = agent_id × 1111.

Finding: Information disclosure of identifier-generation algorithms in client-side code.


Phase 2 — Pre-Auth Data Exposure (Critical)

The magic-link endpoint /api/auth/magic is a token-based authentication endpoint by design — so it doesn't require any session. The vulnerability: it accepts any sequential token, not just one issued to a specific user.

curl -s "http://localhost:8888/api/auth/magic?token=magic-000001-sindel"

Unauthenticated bypass

The response contains agent #1's full dossier: name, DOB, phone, bank account, safe house GPS coordinates, and the flag [REDACTED].

Incrementing the token from magic-000001-sindel through magic-000005-sindel enumerated every agent in the system with zero authentication.

Impact: Full PII disclosure of every operative, including operational details like safe house coordinates. No credentials required.


Phase 3 — Foothold

The magic-link enumeration revealed agent #5 (codename SHADOW, real name Yaw Asante). The bio gave it away:

"New recruit undergoing field training. Standard entry point."

I guessed the default password using the regional naming convention {FirstName}@2025 (the application branding indicates Ghanaian origin). Hit on the first try.

Foothold

Now authenticated as the lowest-privilege user (Recruit, ALPHA clearance). Every IDOR test from here forward proves that a low-privilege user can read higher-privilege users' data — the textbook IDOR demonstration.


Phase 4 — Systematic IDOR Sweep

I targeted agent #1 (PHANTOM, Senior Operative, OMEGA clearance) for every IDOR class. The API exposed the same agent data through 15+ different lookup endpoints, each using a different identifier type:

Class Endpoint Identifier
Numeric ID /api/agents/1 Sequential integer
UUID /api/missions/uuid/{uuid} Mission UUID
Phone /api/agents/phone/+233201110001 Ghana phone format
Email /api/agents/email/kira@sindel.ops Email address
Username /api/agents/u/phantom_k firstname_l pattern
Slug /api/intel/phantom-intel-alpha {codename}-intel-alpha
Base64 /api/access/b64/MQ== base64("1")
Hex /api/access/hex/31 hex("1")
URL /api/access/url/agent_1 agent_{id}
MD5 /api/agents/hash/md5/... MD5(email)
SHA1 /api/agents/hash/sha1/... SHA1(username)
File Path /api/files/read?path=agents/agent_001.txt File system path
Magic Link /api/auth/magic?token=... magic-{id}-sindel
Reset Token /api/auth/reset base64("reset:{id}:sindel")
QR Token /api/auth/qr/QR-00001337 QR-{id*1337}

Every endpoint returned data the Recruit should not have access to.

The lesson: Encoding or hashing an identifier (Base64, hex, MD5, SHA1) doesn't make it a secret — it's a transformation of public data. The fix isn't a stronger identifier; it's a server-side authorization check on every request.


Phase 5 — Composite IDOR + Brute Force

Three endpoints required two fields together, designed to feel safer:

Two of them fell immediately — I already had everyone's phone, DOB, name, and email from the magic-link leak.

The third (/api/verify/code) used a 4-digit code with a hinted formula (id × 1111). The hint turned out to be wrong (code for agent 1 was actually 2111), but with no rate limiting I brute-forced the full 10,000-code space in under a minute.

Brute force success

Lesson: Combining two pieces of low-entropy, leakable data doesn't create real authentication. Short numeric codes without rate limiting are equivalent to no code at all.


Phase 6 — Privilege Escalation & Account Takeover

The password-reset endpoint had a critical secondary bug: it accepted an optional new_password parameter alongside the forgeable token. Combined with the predictable token format (base64("reset:{id}:sindel")), that's a full account takeover primitive.

The attack chain:

  1. Forge reset token for agent 1: base64("reset:1:sindel") = cmVzZXQ6MTpzaW5kZWw=
  2. Hijack PHANTOM by sending the token + new_password
  3. Log in as PHANTOM with the new password
  4. Dump PHANTOM's classified missions (a Recruit can't see them)
  5. Steal the mission UUID
  6. Switch back to the Recruit JWT and call /api/missions/uuid/{stolen-uuid} — that triggers the final UUID-based IDOR flag

Privilege escalation chain

The exfiltrated mission: Operation Nightfall"Neutralise HVT in grid 77A", target Asset Kofi Mensah. TOP SECRET classification, accessed by a Recruit through one chained vulnerability.

Lesson: Multiple individually-minor bugs (forgeable tokens, missing authorization, no email confirmation on password reset) chain together into complete account takeover. The whole is greater than the sum of its parts.


All 18 Flags

# Class Flag
I1 Numeric ID [REDACTED]
I2 UUID Mission [REDACTED]
I3 Phone [REDACTED]
I4 Email [REDACTED]
I5 Username [REDACTED]
I6 Slug [REDACTED]
I7a Phone + DOB [REDACTED]
I7b Email + Code [REDACTED]
I7c Name + Birth [REDACTED]
I8 File Path [REDACTED]
I9a Base64 [REDACTED]
I9b Hex [REDACTED]
I9c URL [REDACTED]
I10a MD5(email) [REDACTED]
I10b SHA1(username) [REDACTED]
I11a Magic Link [REDACTED]
I11b Reset Token [REDACTED]
I11c QR Token [REDACTED]

Key Takeaways

  1. Authentication ≠ authorization. Verifying that a user is logged in says nothing about whether they should access a specific resource. Every endpoint that accepts an identifier needs a server-side check: "does this user own / have rights to this object?"

  2. Encoded identifiers are not secrets. Base64, hex, MD5, SHA1 of public data is still public data. An attacker who knows the input pattern derives the identifier trivially.

  3. Sequential/predictable tokens are guessable IDs in disguise. Magic links, password reset tokens, and QR codes must be cryptographically random and single-use.

  4. Client-side code is public. Anything in your HTML or JavaScript — including identifier-generation algorithms, API maps, and "hidden" hints — is reconnaissance material for an attacker.

  5. Rate limiting matters. A 4-digit code with no rate limiting offers no security. Lockout, exponential backoff, or CAPTCHA on sensitive verification endpoints.

  6. Bug chains kill. Three individually-low-severity issues (predictable token, missing auth on reset, optional new_password field) combined into a full account takeover.


Tools Used


Lab Source

SPECIALITY-PRO-LABS / sindel-ctf — IDOR-focused training environment.

Designed for educational purposes. All exploitation performed on a local Docker container.