Proces rozwoju exploita

https://chacker.pl/

Teraz, gdy omówiliśmy podstawy, jesteś gotowy, aby przyjrzeć się rzeczywistemu przykładowi. W rzeczywistym świecie luki nie zawsze są tak proste, jak przykład meet.c. Proces rozwoju exploita przepełnienia stosu zazwyczaj przebiega według następujących kroków:

  1. Kontroluj przepływ wykonywania (rejestr EIP) poprzez identyfikację luki, która powoduje przepełnienie adresu powrotnego.
  2. Określ przesunięcie(a) i ograniczenia (złe znaki przerywające exploit, takie jak znaki nowego wiersza, powrotu karetki i bajty zerowe).
  3. Określ wektor ataku.
  4. Debuguj i śledź przepływ programu podczas przepełnienia.
  5. Zbuduj exploit.
  6. Przetestuj exploit.

Każda luka będzie miała własne ograniczenia i specjalne sytuacje w zależności od charakteru podatnego programu, flag czasu kompilacji, zachowania i przyczyny głównej podatnej funkcji oraz sposobu, w jaki przekształca ona dane wejściowe powodujące exploit.

Wykorzystywanie małych buforów

https://chacker.pl/

Co się stanie, jeśli podatny bufor będzie zbyt mały, aby użyć bufora exploita, jak opisano wcześniej? Co jeśli znaleziony podatny bufor ma długość tylko 10 bajtów? Przyjrzyjmy się następującemu podatnemu kodowi:

Skompiluj i ustaw bit SUID:

Teraz, gdy mamy taki program, jak moglibyśmy go wykorzystać? Odpowiedź leży w użyciu zmiennych środowiskowych. Możesz przechowywać swój kod powłoki w zmiennej środowiskowej, a następnie wskazać EIP na tę zmienną środowiskową. Zacznijmy od ustawienia zmiennej środowiskowej o nazwie SHELLCODE:

Następnie musimy uzyskać adres wskazujący na tę zmienną środowiskową. Możemy użyć polecenia gdb x/20s *((char **)environ), ale przesunięcia będą inne w tym środowisku. Inną opcją jest wywołanie libc.getenv z Pythona przy użyciu ctypes, ale niestety Python 64-bitowy nie może załadować bibliotek 32-bitowych. Naszą najszybszą opcją jest napisanie małego programu C, który wywoła getenv(“SHELLCODE”):

Skompiluj i uruchom getenv.c:

Zanim napiszemy nasz exploit, otwórzmy smallbuf za pomocą gdb i sprawdźmy, ile bajtów musimy zapisać, aby nadpisać EIP:

Teraz, gdy wiemy, że potrzebujemy 18 bajtów, aby nadpisać EIP, zakończmy i uruchommy nasz exploit:

Wykorzystywanie Stack Overflow z wiersza poleceń

https://chacker.pl/

Pamiętaj, że w laboratorium 10-1 rozmiar potrzebny do nadpisania EIP w meet.c wynosi 412. Dlatego użyjemy Pythona do stworzenia naszego exploita. Najpierw wyłączmy ASLR dla tego laboratorium, wykonując następujące polecenie:

Teraz użyjmy printf i wc do obliczenia rozmiaru naszego kodu powłoki:

Następnie użyjemy gdb, aby znaleźć miejsce, w którym należy wskazać EIP, aby wykonać nasz kod powłoki. Wiemy już, że możemy nadpisać EIP 412 bajtami, więc naszym pierwszym krokiem jest załadowanie i zawieszenie pliku binarnego z gdb. Aby to zrobić, wydamy następujące polecenie:

Następnie użyjemy gdb, aby znaleźć miejsce, w którym należy wskazać EIP, aby wykonać nasz kod powłoki. Wiemy już, że możemy nadpisać EIP 412 bajtami, więc naszym pierwszym krokiem jest załadowanie i zawieszenie pliku binarnego z gdb. Aby to zrobić, wydamy następujące polecenie:

Następnie użyjemy gdb, aby znaleźć miejsce, w którym należy wskazać EIP, aby wykonać nasz kod powłoki. Wiemy już, że możemy nadpisać EIP 412 bajtami, więc naszym pierwszym krokiem jest załadowanie i zawieszenie pliku binarnego z gdb. Aby to zrobić, wydamy następujące polecenie:

Shellcode

https://chacker.pl/

Shellcode to termin zarezerwowany dla kodu maszynowego, który wykona polecenia hakera. Pierwotnie termin ten został wymyślony, ponieważ celem złośliwego kodu było dostarczenie atakującemu prostej powłoki. Od tego czasu termin ten ewoluował, obejmując kod, który jest używany do zrobienia czegoś więcej niż dostarczenia powłoki, na przykład do podniesienia uprawnień lub wykonania pojedynczego polecenia w zdalnym systemie. Ważne jest, aby zdać sobie sprawę, że kod powłoki jest w rzeczywistości ciągiem binarnych kodów operacji dla eksploatowanej architektury (w tym przypadku Intel x86 32 bit), często reprezentowanych w formie szesnastkowej. W Internecie można znaleźć mnóstwo bibliotek kodu powłoki, gotowych do użycia na wszystkich  platformach. Użyjemy kodu powłoki Aleph1 (pokazanego w programie testowym) w następujący sposób:

Skompilujmy i uruchommy program testowy shellcode.c

Zadziałało — otrzymaliśmy powłokę główną.

NOP Sled

https://chacker.pl/

W kodzie asemblera polecenie NOP (no operation) oznacza po prostu, aby nic nie robić, tylko przejść do następnego polecenia. Hakerzy nauczyli się używać NOP do wypełnienia. Umieszczone na początku bufora exploita wypełnienie to nazywa się NOP sled. Jeśli EIP jest skierowane do NOP sled, procesor przesunie się na sled bezpośrednio do następnego komponentu. W systemach x86 kod operacji 0x90 oznacza NOP. W rzeczywistości jest ich znacznie więcej, ale 0x90 jest najczęściej używany. Każda sekwencja operacji, która nie koliduje z wynikiem exploita, byłaby uważana za równoważną NOP.

Wykorzystywanie lokalnego przepełnienia bufora

https://chacker.pl/

Jednym z głównych celów wykorzystywania lokalnego przepełnienia bufora jest kontrolowanie EIP w celu uzyskania wykonania dowolnego kodu w celu osiągnięcia eskalacji uprawnień. W tej sekcji omówimy niektóre z najczęstszych luk w zabezpieczeniach i sposoby ich wykorzystania

 

Przepełnienie meet.c

https://chacker.pl/

Program meet.c wgląda  następująco:

Użyjemy Pythona, aby przepełnić bufor 400-bajtowy w meet.c. Python jest językiem interpretowanym, co oznacza, że ​​nie trzeba go prekompilować, co czyni go bardzo wygodnym w użyciu w wierszu poleceń. Na razie musisz zrozumieć tylko jedno polecenie Pythona:

To polecenie po prostu wydrukuje 600 A na standardowym wyjściu (stdout) — spróbuj!

UWAGA Znaki odwrotnego apostrofu (`) są używane do opakowania polecenia i wykonania polecenia przez interpreter powłoki oraz zwrócenia wartości. Skompilujmy i wykonajmy meet.c:

Teraz przekażmy 600 A do programu meet.c jako drugi parametr w następujący sposób:

Zgodnie z oczekiwaniami, Twój 400-bajtowy bufor przepełnił się; miejmy nadzieję, że EIP również. Aby to sprawdzić, uruchom ponownie gdb:

UWAGA: Twoje wartości mogą być inne. Pamiętaj, że próbujemy tu przekazać koncepcję, a nie wartości pamięci.

Nie tylko nie kontrolowaliśmy EIP, ale przenieśliśmy się daleko do innej części pamięci. Jeśli spojrzysz na meet.c, zauważysz, że po funkcji strcpy() w funkcji powitania następuje wywołanie printf(), które z kolei wywołuje vfprintf() w bibliotece libc. Funkcja vfprintf() wywołuje następnie strlen. Ale co mogło pójść nie tak? Masz kilka zagnieżdżonych funkcji, a zatem kilka ramek stosu, z których każda została umieszczona na stosie. Kiedy spowodowałeś przepełnienie, musiałeś uszkodzić argumenty przekazane do funkcji printf(). Przypomnij sobie z poprzedniej sekcji, że wywołanie i prolog funkcji sprawiają, że stos wygląda jak na poniższej ilustracji:

Jeśli zapiszesz poza EIP, nadpiszesz argumenty funkcji, zaczynając od temp1. Ponieważ funkcja printf() używa temp1, będziesz miał problemy. Aby sprawdzić tę teorię, sprawdźmy ponownie w gdb. Kiedy uruchomimy gdb ponownie, możemy spróbować uzyskać listę źródłową:

W poprzednim pogrubionym wierszu widać, że argumenty funkcji temp1 i temp2 zostały uszkodzone. Wskaźniki wskazują teraz na 0x41414141, a wartości to „” (lub null). Problem polega na tym, że printf() nie przyjmuje wartości null jako jedynego wejścia i dlatego się dławi. Zacznijmy więc od mniejszej liczby A, takiej jak 405, a następnie powoli ją zwiększajmy, aż uzyskamy pożądany efekt:

Jak widać, gdy w gdb występuje błąd segmentacji, wyświetlana jest bieżąca wartość EIP. Ważne jest, aby zdać sobie sprawę, że liczby (400–412) nie są tak ważne jak koncepcja zaczynania od niskiego poziomu i powolnego zwiększania, aż do przepełnienia zapisanego EIP i niczego więcej. Dzieje się tak z powodu wywołania printf bezpośrednio po przepełnieniu. Czasami będziesz mieć więcej miejsca na oddech i nie będziesz musiał się tym zbytnio martwić. Na przykład, gdyby nic nie następowało po podatnym poleceniu strcpy, nie byłoby problemu z przepełnieniem powyżej 412 bajtów w tym przypadku.

UWAGA: Pamiętaj, że używamy tutaj bardzo prostego fragmentu wadliwego kodu; w prawdziwym życiu napotkasz wiele takich problemów. Ponownie, chcemy, abyś zrozumiał koncepcje, a nie liczby wymagane do przepełnienia konkretnego podatnego fragmentu kodu.

Przepełnienia bufora

https://chacker.pl/

eraz, gdy znasz już podstawy, możemy przejść do konkretów. Jak opisano w rozdziale 2, bufory służą do przechowywania danych w pamięci. Nas interesują głównie bufory przechowujące ciągi znaków. Same bufory nie mają mechanizmów ograniczających, które uniemożliwiałyby dodawanie większej ilości danych niż oczekiwano. W rzeczywistości, jeśli jako programista będziesz niedbały, możesz szybko

przekroczyć przydzieloną przestrzeń. Na przykład poniższy kod deklaruje ciąg znaków w pamięci o rozmiarze 10 bajtów:

Co się stanie jeśli wykonasz poniższe polecenie?

Teraz musimy skompilować i wykonać program 32-bitowy. Ponieważ mamy 64-bitowy Kali Linux, najpierw musimy zainstalować gcc-multilib, aby dokonać cross-kompilacji 32-bitowych plików binarnych:

Po zainstalowaniu gcc-multilib następnym krokiem jest kompilacja naszego programu przy użyciu opcji -m32 i -fno-stack-protector w celu wyłączenia ochrony Stack Canary:

UWAGA: W systemach operacyjnych w stylu Linuxa warto zwrócić uwagę na konwencję dotyczącą monitów, która pomaga odróżnić powłokę użytkownika od powłoki roota. Zazwyczaj powłoka roota będzie miała znak # jako część monitu, podczas gdy powłoki użytkownika zazwyczaj mają znak $ w monicie. Jest to wizualna wskazówka, która pokazuje, gdy udało Ci się eskalować swoje uprawnienia, ale nadal będziesz chciał to zweryfikować za pomocą polecenia, takiego jak whoami lub id. Dlaczego otrzymałeś błąd segmentacji? Zobaczmy, uruchamiając gdb (GNU Debugger):

UWAGA Randomizacja układu przestrzeni adresowej (ASLR) działa poprzez losowe ustalanie lokalizacji różnych sekcji programu w pamięci, w tym bazy wykonywalnej, stosu, sterty i bibliotek, co utrudnia atakującemu niezawodne przejście do określonego adresu pamięci. Aby wyłączyć ASLR, uruchom następujące polecenie w wierszu poleceń:$ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space Teraz przyjrzyjmy się atakowi na meet.c.