
Management Wants a Word | TryHackMe Writeup
TryHackMe Management Wants a Word writeup: analyze KAPE, recover DPAPI and Chrome credentials, then open the hidden VeraCrypt vault. Final flag redacted.
Welcome to my Management Wants a Word TryHackMe writeup, part of the Hacker Holidays series. This room is a Windows forensics chain rather than a conventional exploit: we start with a KAPE triage collection, recover local secrets and DPAPI material, decrypt a saved Chrome credential, and use it to open a VeraCrypt volume.
Insider threat rarely looks like a movie scene. More often, someone has legitimate access, hides data in a plain-looking file, and bets that nobody will dig one layer deeper than "weird file in Documents". Here, a user named vera moved company financial data into a VeraCrypt container disguised as backup, then saved its password in Chrome.
> Spoiler warning: this walkthrough reveals the complete solution path and recovered passwords, but the final flag remains masked as THM{REDACTED}.
Walkthrough quick reference
This solution covers:
- KAPE triage and Chrome history,
- SAM, SYSTEM and SECURITY registry hives,
- the LSA
DefaultPasswordsecret, - the user's DPAPI master key,
- Chrome's AES-256-GCM key from
Local State, - the saved credential in
Login Data, - a signatureless VeraCrypt container,
- the PDF invoice that holds the flag.
The dependency chain looks like this:
LSA DefaultPassword (minivera)
-> DPAPI Master Key
-> Chrome Local State (AES key)
-> Login Data (VeraCrypt password)
-> backup.vc -> PDF invoice as an image -> THM{REDACTED}Every step depends on the previous one. You cannot jump straight to the end, and that is the whole point of this lab.
What we start with
The evidence is a classic KAPE collection, meaning selected pieces of the registry and the user profile, without a full disk image. Once you filter out the noise, a few genuinely important things remain:
KAPE/C/
Windows/System32/config/ # SAM, SYSTEM, SECURITY, SOFTWARE
Users/vera/
Documents/backup # ~100 MiB, no recognizable format
AppData/Roaming/Microsoft/Protect/<SID>/ # DPAPI master key
AppData/Local/Google/Chrome For Testing/User Data/
Local State # encrypted Chrome key (DPAPI)
Default/History
Default/Login DataThat is enough. We have the LSA secrets, the user DPAPI key, the Chrome profile and a suspicious container. The rest is step by step work.
Establishing intent: browser history
Before I decrypt anything, I want to know what I am looking for. Chrome history lives in a SQLite database, and the timestamps are stored as FILETIME (microseconds since January 1, 1601). Converting them to a readable date looks like this:
cp ".../Default/History" /tmp/hist.db
sqlite3 /tmp/hist.db \
"SELECT datetime(last_visit_time/1000000-11644473600,'unixepoch'), url
FROM urls ORDER BY last_visit_time;"The result reads like a short story: a login to the internal bytelotus.thm portal, then searches along the lines of "how to exfiltrate data" and "tryhackme". On its own that is not proof, but it sets priorities nicely. Now I know where to keep digging.
The suspicious `backup` file
Inside Documents sits a file that is exactly 100 MiB, has no extension and no recognizable format:
file Users/vera/Documents/backup # data
stat -c %s .../backup # 104857600 (exactly 100 MiB)
xxd .../backup | head # pure entropy, no header at allThree things scream "VeraCrypt or TrueCrypt container":
- No magic bytes. The header is encrypted on purpose so it cannot be fingerprinted by signature.
- High, uniform entropy. The content looks like pure noise.
- A perfectly round size. Someone manually defined a 100 MiB container.
The catch is that without a password this is a dead end. And the password, as it turns out, is sitting in Chrome.
Chrome Login Data and the v10 blob
Chrome keeps saved passwords in the Login Data database. I pull the interesting record:
cp ".../Default/Login Data" /tmp/login.db
sqlite3 /tmp/login.db \
"SELECT origin_url, username_value, hex(password_value) FROM logins;"I get this entry:
| Field | Value |
|---|---|
| URL | http://bytelotus.thm:8080/ |
| User | VeraSecretVault |
| Password | hex starting with 763130, which is ASCII v10 |
Since a certain Chromium version the v10 prefix means AES-256-GCM, laid out as v10 || IV(12 bytes) || ciphertext || tag(16 bytes). The key for that GCM is not in the database, though. It is wrapped by DPAPI and tucked away in the Local State file. I have to recover it first.
Decryption, layer by layer
Layer 1: the LSA secret
Windows autologon can store the user password in LSA Secrets. I extract it offline from the hives:
impacket-secretsdump -sam SAM -system SYSTEM -security SECURITY LOCALThe output contains DefaultPassword in cleartext: `minivera`. That is the pivot into the entire DPAPI store of user vera. For good measure you can verify that MD4(UTF-16LE("minivera")) matches the NT hash from SAM. In the lab it does.
Layer 2: the DPAPI master key
I have the user SID and the master key file from the Protect folder:
S-1-5-21-2529683458-431225740-1723070931-1000
.../Protect/<SID>/c90719ef-5b98-474e-b934-136d606a702aDPAPI derives its operational key from the user password and their SID, so with both in hand I recover the master key:
impacket-dpapi masterkey \
-file "$MK" -sid "$SID" -password miniveraThe output is a decrypted master key starting with 0x5e5715ec9b6df5a86e....
Layer 3: the Chrome AES key from Local State
Chrome stores its encryption key in the Local State file, base64 encoded and wrapped by DPAPI. The first 5 bytes are a DPAPI marker that has to be stripped:
import json, base64
ls = json.load(open(".../Local State"))
raw = base64.b64decode(ls["os_crypt"]["encrypted_key"])
open("/tmp/chrome_key_blob.bin", "wb").write(raw[5:])Now I unwrap that blob with the DPAPI master key recovered earlier:
impacket-dpapi unprotect -file /tmp/chrome_key_blob.bin -key 0x5e57...The result is the 32-byte Chrome AES-256 key:
206a39a0971327ea9487e4aea9844f5d3670162456982276939a712646da0b02Layer 4: the vault password
I have the key and I have the blob from the database. I take apart the GCM structure and decrypt:
from Crypto.Cipher import AES
key = bytes.fromhex("206a39a0...")
blob = bytes.fromhex("763130...") # value from Login Data
iv, ct, tag = blob[3:15], blob[15:-16], blob[-16:]
print(AES.new(key, AES.MODE_GCM, nonce=iv).decrypt_and_verify(ct, tag))And out comes the password in cleartext:
Wh4t1sV3raD0inG0nTh1sH0stThat is the passphrase for the backup container. The chain is complete.
Opening VeraCrypt and finding the flag
On Linux the easiest path is the built-in TrueCrypt/VeraCrypt support in cryptsetup:
echo -n 'Wh4t1sV3raD0inG0nTh1sH0st' | \
cryptsetup --type tcrypt --veracrypt open /tmp/backup.vc vaultvol
mount -o ro /dev/mapper/vaultvol /mnt/vaultI did this on macOS without FUSE and without cryptsetup, so I went the userspace route: PBKDF2-HMAC-SHA512 with 500,000 iterations over the salt from the header, then AES-XTS on the header (after decryption the magic VERA shows up), followed by AES-XTS over the whole payload starting at offset 0x20000. The result is a clean FAT32 image, which I then unpacked with plain 7z.
Inside, two files:
secret_financial_documents/
transactions_q3.csv
important_invoice_byte_lotus.pdfThe CSV looks harmless, but one row is a clear hint:
2026-07-12, TXN-10531, Internal Adjustment, Image asset correction, 0.00, Archived"Image asset correction" is not an accident. pdftotext returns nothing useful on the invoice, because the content is rendered there as an image rather than text. So you have to extract the image itself:
pdfimages -all important_invoice_byte_lotus.pdf /tmp/inv
# alternatively: mutool draw -r 200 -o /tmp/inv.png ...On the rendered Byte Lotus Resorts invoice, in the description of line item number 1, sits the flag:
THM{REDACTED}I am not spelling it out in full here, because that would spoil the fun for anyone who wants to solve this lab on their own. The path you take matters more than the exact string at the end.
What this means for the defender
A few takeaways worth carrying into everyday DFIR work:
- Autologon plus LSA Secrets is a cleartext user password sitting right there in the triage. Treat the
SECURITYandSYSTEMhives as high-value from the first minute. - DPAPI is not a magic barrier. If you have the user password (or backup keys, or domain context) you can reverse it offline. And Chrome on Windows builds its
os_crypton exactly that. - Passwords saved in the browser are often keys to further layers: VPN, vaults, containers, cloud. A single convenient "remember password" can unravel a whole defense.
- You recognize a signatureless container by context, not by magic bytes. Size, entropy, path, browser history and the password store say more than the file header.
- Data hidden in an image slips past a plain
grep. An invoice as an image, a screenshot or a scan will pass unnoticed until you reach for OCR or graphics extraction. The hint in the CSV was placed there on purpose.
For a red team the moral is mirrored: even solid encryption will not protect data if the secret to it lives in autologon and in the browser.
Tooling cheat sheet
| Step | Tool |
|---|---|
| LSA and SAM secrets | impacket-secretsdump |
| DPAPI master key and unprotect | impacket-dpapi |
| Chrome databases | sqlite3 |
| GCM password decryption | pycryptodome |
| VeraCrypt | cryptsetup (tcrypt/veracrypt mode) or userspace AES-XTS |
| Pulling files from FAT | 7z, mount, sleuthkit |
| PDF to image | pdfimages, mutool |
| OCR when needed | tesseract |
Wrap-up
This investigation does not start with breaking VeraCrypt. It starts with identity and system secrets. Autologon leads to DPAPI, DPAPI to the Chrome key, the key to the vault password, the password to the container, and the container to an invoice saved as an image. The flag sat at the very end, but without each earlier step it was out of reach.
If you build your own labs, this scenario shows one thing really well: endpoint encryption and browser secrets are a single ecosystem. And a well collected KAPE triage can put it back together even without a full disk image.