
Management Wants a Word | TryHackMe Writeup po polsku
Polski writeup TryHackMe Management Wants a Word: analiza KAPE, DPAPI, hasła Chrome i kontenera VeraCrypt. Końcowa flaga pozostaje ukryta.
To jest mój polski writeup do pokoju TryHackMe Management Wants a Word z serii Hacker Holidays. Zamiast typowego exploita dostajemy komplet artefaktów Windows zebranych przez KAPE i odtwarzamy cały łańcuch forensics: od lokalnych sekretów użytkownika, przez DPAPI i Chrome, aż po zaszyfrowany wolumen VeraCrypt.
Insider threat rzadko wygląda jak scena z filmu. Zwykle ktoś ma legalny dostęp, chowa dane w niczym niewyróżniającym się pliku i liczy, że nikt nie zejdzie warstwę niżej niż "dziwny plik w Dokumentach". Tutaj użytkowniczka vera wyniosła dane finansowe do kontenera udającego zwykły plik backup, a hasło zapisała w Chrome.
> Uwaga na spoilery: pokazuję pełną ścieżkę rozwiązania i odzyskane hasła, ale końcową flagę pozostawiam jako THM{REDACTED}.
Szybka mapa rozwiązania
W tym walkthrough pojawiają się:
- triage KAPE i historia Chrome,
- hive'y SAM, SYSTEM i SECURITY,
- sekret LSA
DefaultPassword, - DPAPI master key użytkownika,
- klucz AES-256-GCM z Chrome
Local State, - zapisane hasło z bazy
Login Data, - kontener VeraCrypt bez magic bytes,
- faktura PDF zawierająca flagę.
Cały łańcuch zależności wygląda tak:
LSA DefaultPassword (minivera)
-> DPAPI Master Key
-> Chrome Local State (klucz AES)
-> Login Data (hasło do VeraCrypt)
-> backup.vc -> faktura PDF jako obraz -> THM{REDACTED}Każdy krok zależy od poprzedniego. Nie da się przeskoczyć od razu do końca, i o to właśnie chodzi w tym labie.
Co dostajemy na start
Materiał dowodowy to klasyczna kolekcja KAPE, czyli wybrane kawałki rejestru i profilu użytkownika, bez pełnego obrazu dysku. Po odsianiu szumu zostaje kilka naprawdę istotnych rzeczy:
KAPE/C/
Windows/System32/config/ # SAM, SYSTEM, SECURITY, SOFTWARE
Users/vera/
Documents/backup # ~100 MiB, bez rozpoznawalnego formatu
AppData/Roaming/Microsoft/Protect/<SID>/ # DPAPI master key
AppData/Local/Google/Chrome For Testing/User Data/
Local State # zaszyfrowany klucz Chrome (DPAPI)
Default/History
Default/Login DataTo wystarczy. Mamy sekrety LSA, klucz DPAPI użytkownika, profil Chrome i podejrzany kontener. Reszta to już praca krok po kroku.
Ustalamy intencję: historia przeglądarki
Zanim zacznę cokolwiek deszyfrować, chcę wiedzieć, czego szukam. Historia Chrome siedzi w bazie SQLite, a znaczniki czasu są w formacie FILETIME (mikrosekundy od 1 stycznia 1601). Konwersja do czytelnej daty wygląda tak:
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;"Wynik układa się w opowieść: logowanie do wewnętrznego portalu bytelotus.thm, a potem wyszukiwania w stylu "how to exfiltrate data" i "tryhackme". Sam w sobie to jeszcze nie dowód, ale świetnie porządkuje priorytety. Wiadomo, gdzie szukać dalej.
Podejrzany plik `backup`
W Dokumentach leży plik o rozmiarze dokładnie 100 MiB, bez rozszerzenia i bez rozpoznawalnego formatu:
file Users/vera/Documents/backup # data
stat -c %s .../backup # 104857600 (dokładnie 100 MiB)
xxd .../backup | head # sama entropia, zero nagłówkaTrzy rzeczy krzyczą "kontener VeraCrypt lub TrueCrypt":
- Brak magic bytes. Nagłówek jest zaszyfrowany celowo, żeby nie dało się go rozpoznać po sygnaturze.
- Wysoka i równa entropia. Zawartość wygląda jak czysty szum.
- Idealnie okrągły rozmiar. Ktoś ręcznie zdefiniował kontener na 100 MiB.
Problem w tym, że bez hasła to ślepa uliczka. A hasło, jak się okazuje, siedzi w Chrome.
Chrome Login Data i blob v10
Zapisane hasła Chrome trzyma w bazie Login Data. Wyciągam interesujący rekord:
cp ".../Default/Login Data" /tmp/login.db
sqlite3 /tmp/login.db \
"SELECT origin_url, username_value, hex(password_value) FROM logins;"Dostaję wpis:
| Pole | Wartość |
|---|---|
| URL | http://bytelotus.thm:8080/ |
| Użytkownik | VeraSecretVault |
| Hasło | hex zaczynający się od 763130, czyli ASCII v10 |
Od pewnej wersji Chromium prefiks v10 oznacza AES-256-GCM w układzie v10 || IV(12 bajtów) || szyfrogram || tag(16 bajtów). Klucz do tego GCM nie leży jednak w bazie. Jest owinięty przez DPAPI i schowany w pliku Local State. Trzeba go najpierw odzyskać.
Deszyfracja warstwa po warstwie
Warstwa 1: sekret LSA
Windowsowy autologon potrafi trzymać hasło użytkownika w LSA Secrets. Wyciągam je offline z hive'ów:
impacket-secretsdump -sam SAM -system SYSTEM -security SECURITY LOCALW wyniku pojawia się DefaultPassword w postaci jawnej: `minivera`. To jest punkt zaczepienia do całego DPAPI użytkownika vera. Dla pewności można jeszcze zweryfikować, że MD4(UTF-16LE("minivera")) zgadza się z hashem NT z SAM. W labie się zgadza.
Warstwa 2: DPAPI Master Key
Mam SID użytkownika i plik master key z folderu Protect:
S-1-5-21-2529683458-431225740-1723070931-1000
.../Protect/<SID>/c90719ef-5b98-474e-b934-136d606a702aDPAPI wyprowadza klucz operacyjny z hasła użytkownika i jego SID, więc mając oba, odzyskuję master key:
impacket-dpapi masterkey \
-file "$MK" -sid "$SID" -password miniveraNa wyjściu odszyfrowany master key zaczynający się od 0x5e5715ec9b6df5a86e....
Warstwa 3: klucz AES Chrome z Local State
Chrome trzyma swój klucz szyfrujący w pliku Local State, zakodowany w base64 i owinięty przez DPAPI. Pierwsze 5 bajtów to marker DPAPI, który trzeba obciąć:
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:])Teraz odwijam ten blob odzyskanym wcześniej master keyem DPAPI:
impacket-dpapi unprotect -file /tmp/chrome_key_blob.bin -key 0x5e57...W efekcie dostaję 32-bajtowy klucz AES-256 Chrome:
206a39a0971327ea9487e4aea9844f5d3670162456982276939a712646da0b02Warstwa 4: hasło do sejfu
Mam klucz, mam blob z bazy. Rozbieram strukturę GCM i deszyfruję:
from Crypto.Cipher import AES
key = bytes.fromhex("206a39a0...")
blob = bytes.fromhex("763130...") # wartość z 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))I wyskakuje hasło w postaci jawnej:
Wh4t1sV3raD0inG0nTh1sH0stTo jest passphrase do kontenera backup. Cały łańcuch domknięty.
Otwieramy VeraCrypt i szukamy flagi
Na Linuxie najprościej użyć wbudowanego wsparcia dla TrueCrypt/VeraCrypt w cryptsetup:
echo -n 'Wh4t1sV3raD0inG0nTh1sH0st' | \
cryptsetup --type tcrypt --veracrypt open /tmp/backup.vc vaultvol
mount -o ro /dev/mapper/vaultvol /mnt/vaultJa robiłem to na macOS bez FUSE i cryptsetup, więc poszedłem drogą w przestrzeni użytkownika: PBKDF2-HMAC-SHA512 z 500 000 iteracji na soli z nagłówka, potem AES-XTS na nagłówku (po deszyfracji pojawia się magiczne VERA), a następnie AES-XTS na całym payloadzie od offsetu 0x20000. Wychodzi z tego czysty obraz FAT32, który dalej rozpakowałem zwykłym 7z.
W środku dwa pliki:
secret_financial_documents/
transactions_q3.csv
important_invoice_byte_lotus.pdfCSV wygląda niewinnie, ale jeden wiersz to wyraźna wskazówka:
2026-07-12, TXN-10531, Internal Adjustment, Image asset correction, 0.00, Archived"Image asset correction" to nie przypadek. pdftotext na fakturze nie zwraca nic sensownego, bo treść jest tam wrenderowana jako obraz, a nie tekst. Trzeba więc wyciągnąć sam obrazek:
pdfimages -all important_invoice_byte_lotus.pdf /tmp/inv
# alternatywnie: mutool draw -r 200 -o /tmp/inv.png ...Na wyrenderowanej fakturze Byte Lotus Resorts, w opisie pozycji numer 1, siedzi flaga:
THM{REDACTED}Nie zdradzam jej tutaj w całości, bo to psułoby zabawę każdemu, kto chce przejść ten lab samodzielnie. Sam łańcuch dojścia jest ważniejszy niż konkretny ciąg znaków.
Co z tego wynika dla obrońcy
Kilka wniosków, które warto zabrać ze sobą do codziennej pracy DFIR:
- Autologon plus LSA Secrets to gotowe hasło użytkownika w postaci jawnej, dostępne już w samym triage'u. Hive'y
SECURITYiSYSTEMtraktuj jako materiał wysokiej wartości od pierwszej minuty. - DPAPI nie jest magiczną barierą. Jeśli masz hasło użytkownika (albo klucze zapasowe czy kontekst domenowy), da się je odwrócić offline. A Chrome na Windowsie opiera swoje
os_cryptwłaśnie na DPAPI. - Hasła zapisane w przeglądarce to często klucze do kolejnych warstw: VPN, sejfów, kontenerów, chmury. Jedno wygodne "zapamiętaj hasło" potrafi rozłożyć całą obronę.
- Kontener bez sygnatury rozpoznajesz po kontekście, a nie po magic bytes. Rozmiar, entropia, ścieżka, historia przeglądarki i baza haseł mówią więcej niż nagłówek pliku.
- Dane schowane w obrazku wymkną się zwykłemu
grep. Faktura jako obraz, screenshot czy skan przejdą bez echa, dopóki nie sięgniesz po OCR albo ekstrakcję grafiki. Wskazówka w CSV była tu położona celowo.
Dla red teamu morał jest lustrzany: nawet porządne szyfrowanie nie obroni danych, jeśli sekret do niego leży w autologonie i w przeglądarce.
Ściąga narzędziowa
| Krok | Narzędzie |
|---|---|
| Sekrety LSA i SAM | impacket-secretsdump |
| DPAPI master key i unprotect | impacket-dpapi |
| Bazy Chrome | sqlite3 |
| Deszyfracja hasła GCM | pycryptodome |
| VeraCrypt | cryptsetup (tryb tcrypt/veracrypt) lub AES-XTS w userspace |
| Wyciąganie plików z FAT | 7z, mount, sleuthkit |
| PDF do obrazu | pdfimages, mutool |
| OCR w razie potrzeby | tesseract |
Podsumowanie
To śledztwo nie zaczyna się od łamania VeraCrypt. Zaczyna się od tożsamości i sekretów systemowych. Autologon prowadzi do DPAPI, DPAPI do klucza Chrome, klucz do hasła sejfu, hasło do kontenera, a kontener do faktury zapisanej jako obraz. Flaga siedziała na samym końcu, ale bez każdego wcześniejszego kroku była nieosiągalna.
Jeśli budujesz własne laby, ten scenariusz świetnie pokazuje jedną rzecz: szyfrowanie endpointu i sekrety przeglądarki to jeden ekosystem. A dobrze zebrany triage KAPE potrafi go złożyć w całość nawet bez pełnego obrazu dysku.