Konfiguracja podatnego programu i środowiska

https://chacker.pl/

Najpierw przeanalizujmy podatny program, którego będziemy używać w tym rozdziale. Program vuln.c jest dostarczany w folderze ~/GHHv6/ch11, a w każdym laboratorium będziemy go kompilować ponownie, umożliwiając różne techniki łagodzenia zagrożeń. Podatny program to prosty wielowątkowy serwer TCP, który żąda od użytkownika podania hasła z prostą podatnością na przepełnienie stosu w funkcji auth. Zacznijmy od skompilowania programu vuln.c tylko z ochroną przed niewykonywalnym stosem (NX):

Aby sprawdzić, czy usługa działa, uruchommy ją w tle i użyjmy netcata, aby się z nią połączyć:

Wyłączymy losową modulację układu przestrzeni adresowej (ASLR), aby skupić się na pominięciu NX, a następnie włączymy ją ponownie

Zaawansowane eksploity Linuksa

https://chacker.pl/

Teraz, gdy masz już podstawy , jesteś gotowy, aby studiować bardziej zaawansowane eksploity Linuksa. Dziedzina ta stale się rozwija, a hakerzy ciągle odkrywają nowe techniki, a programiści wdrażają środki zaradcze. Niezależnie od tego, jak podchodzisz do problemu, musisz wyjść poza podstawy. Powiedziawszy to, możemy zajść tylko tak daleko w tej książce — twoja podróż dopiero się zaczyna.

Podsumowanie

https://chacker.pl/

Podczas eksploracji podstaw exploitów Linuksa, zbadaliśmy kilka sposobów na pomyślne przepełnienie bufora w celu uzyskania podwyższonych uprawnień lub zdalnego dostępu. Wypełniając więcej miejsca niż bufor przydzielił, możemy nadpisać Extended Stack Pointer (ESP), Extended Base Pointer (EBP) i Extended Instruction Pointer (EIP), aby kontrolować elementy wykonywania kodu. Powodując przekierowanie wykonania do kodu powłoki, który dostarczamy, możemy przejąć wykonywanie tych plików binarnych, aby uzyskać dodatkowy dostęp. Upewnij się, że ćwiczysz i rozumiesz koncepcje wyjaśnione w tym rozdziale. W następnym rozdziale, obejmującym zaawansowane exploity Linuksa, skupimy się na bardziej zaawansowanych i nowoczesnych koncepcjach eksploatacji 64-bitowego Linuksa.

Budowanie exploita

https://chacker.pl/

Doświadczony badacz może łatwo napisać własny shellcode exploita od podstaw; my jednak po prostu skorzystamy z pakietu shellcraft Pwntools. Jednym z wielu przydatnych shellcode’ów, które zawiera, jest funkcja findpeersh. Znajdzie ona deskryptor pliku naszego bieżącego połączenia gniazda i uruchomi na nim wywołanie systemowe dup2, aby przekierować standardowe wejście i wyjście przed uruchomieniem powłoki:

Uruchommy ponownie gdb i uruchommy nasz exploit; powinniśmy odzyskać naszą powłokę:

Zadziałało! Po uruchomieniu exploita odzyskaliśmy powłokę na naszym własnym połączeniu. Teraz możemy wykonywać polecenia w naszej interaktywnej powłoce.

Określanie wektora ataku

https://chacker.pl/

Gdy już wiemy, gdzie EIP jest nadpisywane, musimy określić, na jaki adres na stosie musimy wskazać, aby wykonać ładunek. Aby to zrobić, modyfikujemy nasz kod, aby dodać sanki NOP. Daje nam to większy obszar do skoku, tak że jeśli wydarzy się coś drobnego i nasza lokalizacja trochę się zmieni, nadal wylądujemy gdzieś w naszych instrukcjach NOP. Dodając 32 NOP, powinniśmy nadpisać ESP i mieć dodatkową elastyczność dla adresów, do których można przeskoczyć. Pamiętaj, że żaden adres zawierający „\x00” nie zadziała, ponieważ jest to traktowane jako zakończenie ciągu.

Po ponownym uruchomieniu gdb i uruchomieniu nowego kodu exploita powinniśmy zobaczyć, że EIP jest nadpisany przez 0x42424242 (BBBB). Dzięki nowym zmianom powinniśmy móc sprawdzić nasz stos, aby zobaczyć, gdzie znajduje się NOP sled:

Widzimy, że EIP został nadpisany w (1). W (2) wartości są wypełnione naszymi instrukcjami NOP. Jeśli wskoczymy do środka naszego NOP sled w 0xffffd418 (3), powinno to nas doprowadzić bezpośrednio do naszego shellcode.

Określanie offsetu(ów)

https://chacker.pl/

Mając kontrolę nad rejestrem EIP, musimy dowiedzieć się dokładnie, ile znaków zajęło czyste nadpisanie go (i nic więcej). Najłatwiejszym sposobem na zrobienie tego jest użycie generatora wzorców cyklicznych Pwntools. Najpierw utwórzmy skrypt Pythona, aby połączyć się z naszym nasłuchem:

Gdy ponownie uruchomimy nasz plik binarny w gdb i uruchomimy skrypt Pythona w innym oknie, nadal powinniśmy doświadczyć awarii. Jeśli tak się stanie, skrypt Pythona działa prawidłowo, a błąd segmentacji powinien zostać spowodowany ustawieniem EIP na nieprawidłowy adres pamięci 0x41414141 (AAAA). Następnie chcemy dokładnie ustalić, ile znaków potrzeba, aby przepełnić bufor. Zamiast osiągać to za pomocą odczytywania kodu dezasemblacji, możemy przepełnić program cyklicznym wzorcem: unikalną sekwencją bajtów w ciągu o wstępnie zdefiniowanej długości. Wynikowa wartość nadpisanego EIP będzie odpowiadać czterem unikalnym bajtom w cyklicznym wzorcu, które można łatwo zlokalizować, zapewniając dokładną długość, o jaką powinniśmy uzupełnić nasz kod powłoki, aby osiągnąć przesunięcie zapisanego adresu powrotu w stosie. Użyjemy cyklicznej funkcji Pwntools, aby osiągnąć to w naszym exploicie:

Teraz, gdy uruchamiamy exploit, otrzymujemy inne nadpisanie w gdb:

Tutaj widzimy, że EIP został ustawiony na 0x63616171, co odpowiada sekwencji „caaq” z naszego wzorca cyklicznego. Jeśli zastosujesz się do instrukcji instalacji Pwntools  i wykonasz polecenie sudo pip3 install pwntools, zainstalujesz narzędzia wiersza poleceń Pwntools. Możemy użyć cyklicznego narzędzia wiersza poleceń Pwntools, aby znaleźć przesunięcie odpowiadające 0x63616171:

Jeśli nie chcesz instalować narzędzi wiersza poleceń Pwntools, alternatywą jest uruchomienie konsoli Python3, zaimportowanie Pwntools i użycie funkcji cyclic_find:

Teraz wiemy, że dokładne przesunięcie wynosi 264 bajty, zanim EIP zostanie nadpisane. Daje nam to początkową długość wypełnienia, której potrzebujemy przed wysłaniem lokalizacji nadpisywania EIP.

Tworzenie niestandardowych exploitów

https://chacker.pl/

Tworzenie niestandardowych exploitów.

Sterowanie EIP

Program  jest aplikacją sieciową. Gdy go uruchomimy, będzie nasłuchiwał na porcie 5555:

Podczas testowania aplikacji możemy czasami znaleźć słabości po prostu wysyłając długie ciągi znaków. W innym oknie połączmy się z działającym plikiem binarnym za pomocą netcat:

Teraz użyjmy Pythona, aby utworzyć bardzo długi ciąg i wysłać go jako nazwę użytkownika za pomocą naszego połączenia netcat:

Nasz plik binarny zachowuje się inaczej z długim ciągiem znaków. Aby dowiedzieć się dlaczego, musimy dołączyć gdb. Uruchomimy nasz podatny program w jednym oknie, używając gdb, i wyślemy nasz długi ciąg znaków w innym oknie. Ten program rozwidli proces potomny za każdym razem, gdy zostanie zaakceptowane nowe połączenie. Musisz poinstruować gdb, aby podążał za rozwidlonym procesem potomnym po połączeniu, aby debugować exploit. Możesz to zrobić, uruchamiając set follow-fork-mode child w interfejsie gdb. Rysunek 10-2 pokazuje, co dzieje się na ekranie debugera, gdy wysyłamy długi ciąg znaków. Używając debugera w jednym oknie i naszego długiego ciągu znaków w innym, możemy zobaczyć, że nadpisaliśmy zapisaną ramkę i adres powrotu w pamięci stosu, co skutkuje kontrolą rejestrów EIP i EBP po powrocie z podatnej funkcji

Teraz mamy klasyczne przepełnienie bufora i nadpisaliśmy EIP. To kończy pierwszy krok procesu rozwoju exploita. Przejdźmy do następnego kroku.

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: