Wróćmy do naszego przykładu z saperem. Jakiś czas temu opisałem metodę na 'rozminowanie' z wykorzystaniem informacji znajdujących się w symbolach - wystarczyło sięgnąć do tablicy rgBlk i odpowiednio interpretując dane mieliśmy to, co nas interesowało.
Aby dobrać się do interesujących nas danych nie musimy mieć jednak dostępnych symboli, wystarczy, że będziemy wiedzieć skąd te dane wyłuskać.
Podając przepis na rozminowanie pola przygotowałem tabelkę z symbolami oraz informacją o ich możliwych rozmiarach - przyjrzyjmy się bliżej sąsiadom tablicy rgBlk:
1005330 winmine!cBombStart 4
1005334 winmine!xBoxMac 4
1005338 winmine!yBoxMac 8
1005340 winmine!rgBlk 864
Zauważmy, że moglibyśmy to zapisać w postaci:
typedef struct _Bomby {
DWORD32 cBombStart;
DWORD32 xBoxMac;
DWORD32 yBoxMac;
DWORD32 alignment;
BYTE rgBlk[832];
} Bomby;
Wystarczy zatem, że będziemy wiedzieli gdzie znajduje się jeden z elementów tej struktury, a pozostałe odnajdziemy dodając odpowiedni offset. Przyjrzyjmy się bliżej symbolowi cBombStart:
0:000> x winmine!cBombStart
01005330 winmine!cBombStart = <no type information>
Mamy zatem interesujący nas adres. Troszkę sobie utrudnijmy i sprawdźmy, czy gdzieś w obszarze obrazu winmine nie występuje odwołanie do tego adresu:
0:000> lmvm winmine
start end module name
01000000 01020000 winmine (pdb symbols) c:\websymbols\winmine.pdb\3B7D84751\winmine.pdb
Loaded symbol image file: winmine.exe
Image path: C:\WINDOWS\system32\winmine.exe
Image name: winmine.exe
Timestamp: Fri Aug 17 22:54:13 2001 (3B7D8475)
CheckSum: 0002B641
ImageSize: 00020000
File version: 5.1.2600.0
Product version: 5.1.2600.0
File flags: 0 (Mask 3F)
File OS: 40004 NT Win32
File type: 1.0 App
File date: 00000000.00000000
Translations: 0409.04b0
CompanyName: Microsoft Corporation
ProductName: Microsoft® Windows® Operating System
InternalName: winmine
OriginalFilename: WINMINE.EXE
ProductVersion: 5.1.2600.0
FileVersion: 5.1.2600.0 (xpclient.010817-1148)
FileDescription: Entertainment Pack Minesweeper Game
LegalCopyright: © Microsoft Corporation. All rights reserved.
Następnie przeszukujemy całą intesujący nas zakres
0:000> s -b 01000000 L?0x00020000 0x30 0x53 0x00 0x01
010036c3 30 53 00 01 ff 35 34 53-00 01 e8 6e 02 00 00 ff 0S...54S...n....
010036ff 30 53 00 01 75 c2 8b 0d-38 53 00 01 0f af 0d 34 0S..u...8S.....4
01003721 30 53 00 01 a3 94 51 00-01 89 3d a4 57 00 01 89 0S....Q...=.W...
Sprawdźmy adresy z wyników wyszukiwania:
0:000> ln 010036c3
(0100367a) winmine!StartGame+0x49 | (0100374f) winmine!MakeGuess
0:000> ln 010036ff
(0100367a) winmine!StartGame+0x85 | (0100374f) winmine!MakeGuess
0:000> ln 01003721
(0100367a) winmine!StartGame+0xa7 | (0100374f) winmine!MakeGuess
A zatem odwołanie do cBombStart występuje w funkcji StartGame. Zobaczmy zatem samą funkcję:
0:000> u winmine!StartGame L37
winmine!StartGame:
0100367a a1ac560001 mov eax,dword ptr [winmine!Preferences+0xc (010056ac)]
0100367f 8b0da8560001 mov ecx,dword ptr [winmine!Preferences+0x8 (010056a8)]
01003685 53 push ebx
01003686 56 push esi
01003687 57 push edi
01003688 33ff xor edi,edi
0100368a 3b0534530001 cmp eax,dword ptr [winmine!xBoxMac (01005334)]
01003690 893d64510001 mov dword ptr [winmine!fTimer (01005164)],edi
01003696 750c jne winmine!StartGame+0x2a (010036a4)
01003698 3b0d38530001 cmp ecx,dword ptr [winmine!yBoxMac (01005338)]
0100369e 7504 jne winmine!StartGame+0x2a (010036a4)
010036a0 6a04 push 4
010036a2 eb02 jmp winmine!StartGame+0x2c (010036a6)
010036a4 6a06 push 6
010036a6 5b pop ebx
010036a7 a334530001 mov dword ptr [winmine!xBoxMac (01005334)],eax
010036ac 890d38530001 mov dword ptr [winmine!yBoxMac (01005338)],ecx
010036b2 e81ef8ffff call winmine!ClearField (01002ed5)
010036b7 a1a4560001 mov eax,dword ptr [winmine!Preferences+0x4 (010056a4)]
010036bc 893d60510001 mov dword ptr [winmine!iButtonCur (01005160)],edi
010036c2 a330530001 mov dword ptr [winmine!cBombStart (01005330)],eax
010036c7 ff3534530001 push dword ptr [winmine!xBoxMac (01005334)]
010036cd e86e020000 call winmine!Rnd (01003940)
010036d2 ff3538530001 push dword ptr [winmine!yBoxMac (01005338)]
010036d8 8bf0 mov esi,eax
010036da 46 inc esi
010036db e860020000 call winmine!Rnd (01003940)
010036e0 40 inc eax
010036e1 8bc8 mov ecx,eax
010036e3 c1e105 shl ecx,5
010036e6 f684314053000180 test byte ptr winmine!rgBlk (01005340)[ecx+esi],80h
010036ee 75d7 jne winmine!StartGame+0x4d (010036c7)
010036f0 c1e005 shl eax,5
010036f3 8d843040530001 lea eax,winmine!rgBlk (01005340)[eax+esi]
010036fa 800880 or byte ptr [eax],80h
010036fd ff0d30530001 dec dword ptr [winmine!cBombStart (01005330)]
01003703 75c2 jne winmine!StartGame+0x4d (010036c7)
01003705 8b0d38530001 mov ecx,dword ptr [winmine!yBoxMac (01005338)]
0100370b 0faf0d34530001 imul ecx,dword ptr [winmine!xBoxMac (01005334)]
01003712 a1a4560001 mov eax,dword ptr [winmine!Preferences+0x4 (010056a4)]
01003717 2bc8 sub ecx,eax
01003719 57 push edi
0100371a 893d9c570001 mov dword ptr [winmine!cSec (0100579c)],edi
01003720 a330530001 mov dword ptr [winmine!cBombStart (01005330)],eax
01003725 a394510001 mov dword ptr [winmine!cBombLeft (01005194)],eax
0100372a 893da4570001 mov dword ptr [winmine!cBoxVisit (010057a4)],edi
01003730 890da0570001 mov dword ptr [winmine!cBoxVisitMac (010057a0)],ecx
01003736 c7050050000101000000 mov dword ptr [winmine!fStatus (01005000)],1
01003740 e825fdffff call winmine!UpdateBombCount (0100346a)
01003745 53 push ebx
01003746 e805e2ffff call winmine!AdjustWindow (01001950)
0100374b 5f pop edi
0100374c 5e pop esi
0100374d 5b pop ebx
0100374e c3 ret
Mamy zatem 3 instrukcje, w których występuje odniesienie do cBombStart. Adres cBombStart poprzedzony jest ciągiem:
a3
80 08 80 ff 0d
a3
odpowiednio w pierwszym, drugim oraz trzecim wystąpieniu, przy czym starałem się brać względnie długi ciąg, który nie jest poprzedzony żadnym symbolem. I teraz myk: przeszukujemy interesujący nas obszar pamięci na wystąpienie powyższych ciągów. Pierwszy i trzeci z góry możemy sobie darować - są za krótkie i na pewno występują więcej niż 1 raz, jednak drugi wygląda zachęcająco:
0:000> s -b 01000000 L?0x00020000 80 08 80 ff 0d
010036fa 80 08 80 ff 0d 30 53 00-01 75 c2 8b 0d 38 53 00 .....0S..u...8S.
Sprawdzamy (dodajemy 5 ze względu na 5 bajtów wzorca):
0:000> dd 010036fa+5 L1
010036ff 01005330
i oczywiście wszystko się zgadza!
Mamy tylko 1 wystąpienie, a zatem możemy zaproponować następujący ‘algorytm’ wyszukania zawartości tablicy rgBlk bez użycia symboli:
1. Znajdź położenie w pamięci modułu winmine.exe: adres bazowy oraz rozmiar.
2. Przeszukaj obszar modułu na wystąpienie wzorca 80 08 80 ff 0d
3. Do znalezionego adresu dodaj 5
4. Z tak ustalonego miejsca odczytaj 4-bajtowy adres
5. Znaleziony adres wskazuje na początek struktury, dodaj zatem do niego 16 aby uzyskać początek tablicy przechowującej położenie bomb.
Proste? :)
No to teraz wyobraźcie sobie, że macie dwa procesy: jeden ‘atakowany’ i drugi własny, nad którym macie pełną kontrolę i w przypadku padu nikogo nic nie zaboli. Do własnego procesu ładujecie interesujący was moduł (np. winmine.exe). Z atakowanego odczytujecie interesujące was informacje, kopiujecie w odpowiednie miejsce do własnego procesu, po czym uruchamiacie jakąś funkcję modułu (np. StartGame), która w jakiś sposób korzysta ze skopiowanych danych.
A teraz wyobraźcie sobie, że zamiast winmine.exe sięgacie do procesu lsass.exe i kopiujecie z obszaru zajmowanego przez lsasrv.dll klucze wykorzystywane przez lsass do szyfrowania i deszyfrowania danych, oczywiście znajdując je przy pomocy odpowiednich ‘magicznych’ ciągów bajtów. W swoim procesie ładujecie lsasrv.dll i inicjalizujecie odpowiednie struktury danych tymi odczytanymi z żywego procesu lsass.exe. A teraz swobodnie z nich korzystacie szyfrując i deszyfrując dane tak, jakby to zrobił sam lsass.
Skoro dotarliście do tego miejsca, to posuńcie się krok dalej i wyobraźcie sobie, że odczytujecie dane ze zrzutu pamięci procesu lsass.exe z innego komputera (o tej samej wersji systemu i architekturze), wrzucacie do własnego procesu, w którym załadowaliście sobie lsasrv.dll i wykonujecie te same czynności, które opisałem wyżej.
Abstrakcja? Otóż dokładnie na tej zasadzie działają ostatnie wersje mimikatz (wspomniałem o nim w poprzednim wpisie) - żadnego ładowania dodatkowych bibliotek do procesu lsass.exe - po prostu search & read, a wszystkie operacje wykonywane są w ramach procesu mimikatz.exe, który ładuje lokalnie odpowiednią wersję lsasrv.dll. A jako bonus - dodany przez Benjamina na moją specjalną prośbę - odczytywanie haseł wprost ze zrzutów pamięci :)