Když „neobnovitelná“ data obnovíte za jedno odpoledne
Každý bezpečnostní analytik zná ten pocit.
Zazvoní telefon, zákazník hlásí problém a během několika minut sedíte na vzdálené ploše serveru, o kterém jste ještě před hodinou netušili, že existuje.
Tentokrát šlo o starý Windows file server. Během několika minut z něj zmizelo přibližně 15 GB dat. Několik tisíc souborů bylo pryč a nikdo netušil proč.
Na první pohled to nevypadalo jako ransomware. Spíš jako klasický lidský faktor. Jenže bezpečnostní incidenty mají jednu nepříjemnou vlastnost – často nejsou tím, čím se na první pohled zdají být.
Rozhodl jsem se proto nepodceňovat situaci a začal jsem vyšetřovat.
Na serveru nebyl aktivní Windows File Auditing. V době incidentu bylo připojeno větší množství uživatelů. A zákazník ještě neměl nasazený NetFlow kolektor.
První hodina investigace tak nepřinesla prakticky nic.
A právě tehdy se případ začal stávat zajímavým.
Složka, která mi zkazila večer
Na serveru běžel Bitdefender BEST.
– Incident Sensor mlčel.
– Karanténa byla prázdná.
– EDR nikde neukazoval nic podezřelého.
Procházel jsem jeden log za druhým a postupně si odškrtával všechny slepé uličky.
– Žádný audit.
– Žádná použitelná telemetrie.
– Žádný incident.
Pak jsem si všiml jedné složky.
C:\Program Files\Bitdefender\Endpoint Security\cryptoStorage
Název byl natolik výmluvný, že jsem na chvíli přestal scrollovat.
Otevřel jsem ji.
A v tu chvíli jsem věděl, že ten večer neskončím v rozumnou hodinu.
Adresář obsahoval velké množství souborů vytvořených přesně v době incidentu.
To samo o sobě ještě nemuselo nic znamenat.
Jenže vedle nich ležel soubor crypt.db.
První stopa
Po otevření se ukázalo, že jde o obyčejnou SQLite databázi.
Obsahovala metadata uložených objektů včetně původních cest, velikostí, hashů, SID identifikátorů a dalších atributů.
Po několika SQL dotazech začala vystupovat zajímavá korelace.
Objekty uložené v CryptoStorage odpovídaly konkrétním souborům, které během incidentu zmizely.
A nejen to.
Jejich velikosti byly identické.
To už nebyla náhoda.
Pokud Bitdefender uchovává metadata a zároveň má uložený obsah souborů, proč by neměla existovat možnost jejich obnovy?
Telefonát s podporou
Rozhodl jsem se nehádat s implementací a zeptat se přímo výrobce.
Na Bitdefenderu jsem založil tiket.
Musím říct, že zkušenost s technickou podporou byla velmi příjemná. Diagnostická data se přenesla jediným kliknutím z GravityZone portálu a celý proces fungoval překvapivě hladce.
O něco později přišlo vyjádření antimalware týmu.
– Nešlo o ransomware.
– Nešlo o malware.
– Nešlo o kompromitaci serveru.
To byla dobrá zpráva.
Bez zaznamenané detekce v době incidentu momentálně neexistuje podporovaný způsob ani nástroj, jak tyto soubory obnovit.
Zavřel jsem tiket.
Pak jsem se podíval na monitor.
Pak znovu na tiket.
A pak zase na monitor.
Protože ve vedlejším okně jsem měl otevřený adresář obsahující několik gigabajtů dat, která podle všeho nešlo obnovit.
Druhý den ráno
Kdo někdy dělal reverzní inženýrství ví, že existují problémy, které člověku nedají spát.
Tohle byl přesně jeden z nich.
Druhý den ráno jsem si udělal kafe, otevřel Ghidru a rozhodl se zjistit, jak CryptoStorage skutečně funguje.
Moje původní hypotéza byla poměrně jednoduchá.
Bitdefender uloží soubor -> zašifruje jej -> klíč uloží do databáze nebo jiného interního úložiště -> při obnově provede opačný proces.
Pokud by tomu tak bylo, potřeboval bych najít použitý algoritmus, způsob práce s klíči, inicializační vektor a formát uložených dat.
Jenže žádný klíč se neobjevoval.
Forenzní analýza .bdf souborů
crypt.db
DOC200126-20012026081723.pdf
f6698329ed296fd9f4f0bd76022fba4a.bdf
První kontrola byla čistě forenzní.
PDF size: 3904919 bytes
BDF size: 3904919 bytes
– Velikosti byly identické.
– Bez headeru.
– Bez paddingu.
– Bez jakékoliv režie.
Pak jsem si všiml další zajímavosti.
MD5 hash původního PDF byl:
f6698329ed296fd9f4f0bd76022fba4a
A přesně stejnou hodnotu nesl název .bdf souboru.
To byl první okamžik, kdy jsem začal mít pocit, že jsem blíž pravdě, než jsem si původně myslel.
Když přestane fungovat databáze
Než jsem otevřel disassembler, provedl jsem několik základních kryptoanalytických testů.
Na první pohled vše připomínalo proudovou šifru.
Jenže žádný z běžných scénářů neseděl.
– opakující se XOR klíč
– XOR proti MD5 hashi
– GUID hodnoty z databáze
– analýza opakujících se bloků
Nic.
Všechno vedlo do slepé uličky.
To bývá okamžik, kdy se z forenzní analýzy stává archeologie. Přestanete hledat odpovědi v datech a začnete je hledat v hlavách vývojářů.
Ghidra nelže
DataStorage::AddFile
DataStorage::RestoreBuffer
DataStorage::_InsertDataStorageItemInDatabase
DataStorage::_RunInsertRefQuery
Workflow začalo postupně dávat smysl:
soubor -> .bdtf -> SQLite metadata -> .bdf
Nakonec jsem skončil ve funkci DataStorage::AddFile.
Po několika hodinách dekompilace jsem byl připravený na AES.
Možná Blowfish.
Možná nějakou proprietární šílenost.
Rozhodně jsem nebyl připravený na to, co jsem našel.
Nejkratší „šifrovací algoritmus“, jaký jsem za poslední dobu viděl
buffer[i] = (bVar6 ^ buffer[i]) + cVar11;
bVar6 = bVar6 + 3;
cVar11 = cVar11 + 0x14;
Chvíli jsem si myslel, že jsem něco přehlédl.
Pak jsem funkci přečetl znovu.
Pak potřetí.
Pak jsem si otevřel assembler.
A pak mi došlo, že se právě dívám na celý transformační algoritmus.
– žádný AES.
– žádný klíč.
– žádný IV.
– žádná key derivation.
Pouze XOR a modulární sčítání.
Opakovaně.
Po blocích 64 kB.
Decryptor
def decrypt_chunk(data: bytes) -> bytes:
out = bytearray(len(data))
for i, value in enumerate(data):
xor_key = (0x19 + 3 * i) & 0xFF
add_key = (0x2B + 0x14 * i) & 0xFF
out[i] = ((value - add_key) & 0xFF) ^ xor_key
return bytes(out)
Když jsem decryptor poprvé spustil, přišel okamžik pravdy.
decrypted MD5:
F6698329ED296FD9F4F0BD76022FBA4A
original MD5:
F6698329ED296FD9F4F0BD76022FBA4A
Hash byl identický.
Obnovený dokument byl bajtově přesnou kopií originálu.
Co si z toho odnést
Nejdůležitější závěr celé analýzy není technický detail transformační funkce.
Je to připomínka, že mezi pojmy:
– uložená data
– obfuskovaná data
– kryptograficky chráněná data
existuje zásadní rozdíl.
Z pohledu uživatele mohou všechny tři varianty vypadat stejně.
Z pohledu reverzního inženýra jde ale o tři úplně odlišné světy.
Původním cílem nebylo reverzovat Bitdefender. Cílem bylo zachránit data zákazníka po bezpečnostním incidentu.
Nakonec se ukázalo, že odpověď neleží v databázi crypt.db, ale několik vrstev hlouběji v implementaci DataStorage::AddFile.
A někdy stačí jedno odpoledne, několik šálků kávy a trochu zvědavosti, aby se z „neobnovitelných“ dat stal běžný PDF soubor otevřený v Adobe Readeru.