SKIP TO MAIN CONTENT

[ WRITEUP NODE / FIELD REPORTS ]

SOLVED CHALLENGES & FIELD ANALYSIS

SECURITY RESEARCH KNOWLEDGE TECHNIQUES
B3S/WRITEUPS/LIGHTWEIGHT-DIRECTORY-WEB
← BACK TO ARCHIVE
EVENT: Safaricom PwnZone 2.0 Qualifiers 2026CATEGORY: WebPOINTS: 150 PTS

Lightweight Directory

AUTHORED BY:@bealthguy10/5/2026

Lightweight Directory

150 points. The brief reads: travel light, leave room for discovery. The framing is literal.

The front page is titled ROSTER HQ and presents esports team content: "Manage your player identity and explore the team operations directory." A single login form and nothing else.

Requesting the root redirects directly to /connect, which serves the same form. Response headers indicate Python and Werkzeug 3.1.9, so the application is Flask-based. The page source holds no additional links, comments or hidden fields, only username and password fields posting back to the same location.

Ordinary credential guesses fail in the expected way. admin / admin returns:

Log in to Directory Failed

The recurring term is directory: team operations directory, Lightweight Directory. LDAP is the natural hypothesis on wording alone, with no supporting evidence yet.

A subsequent test with * / * returns a different error:

Password length not acceptable

The two distinct responses establish ordering: a length check precedes the directory lookup. Passwords of length 1, 2 and 3 return the length message; length 4 and above return the directory failure. The field is validated first, and the lookup follows.

Guessing usernames produced nothing. guest, user, admin, player and roster all failed identically, with the same response size and no observable timing difference or other leak.

Filter injection is the next step. Where the filter is constructed by inserting the username directly, a * in the correct position matches every entry. Most closing variants had no effect and returned the standard failure line. One payload behaved differently:

*)(uid=*))(|(uid=*

supplied as the username with an ordinary four-character password. The response was not the login page at all: a 302 redirect to / plus a session cookie for admin. The response size change is the clearest indicator, 4869 bytes on failure against 189 bytes on this attempt.

The request was replayed to confirm, the cookie retained, and / opened with it, yielding a different page entirely:

Welcome, admin Edit player profile Open team report Open team report Logout

Two links to the same report are listed twice, which is untidy but incidental. The report returns the flag as plain text with no HTML wrapper:

safctf{ef30111b835006ade7f00a9a4526d453}

A second authenticated page was also retrieved. /config-update presents "Update Your Access ID" with a single field for a new identifier; posting a test value returns a redirect to the home page. That path was not pursued further, since the report already contained the flag.

The likely mechanism is filter construction of the form (uid=<input>), where the closing parentheses plus the (|(uid=* segment convert the expression into an OR matching the first directory entry, which is admin. Either the password was not checked against the directory at all, or it accepted any value once the user matched; this cannot be determined from the outside. The session asserted admin and the server acted on that assertion.

A more efficient ordering would place filter breakout tests before extended username guessing, given two separate indications that a directory was involved. The duplicated report link is also unexplained. It is plausibly a defect, and on a repeat run the two link targets would be compared for differing parameters.

Flag:

safctf{ef30111b835006ade7f00a9a4526d453}