Learn & practice AWS Hacking:<imgsrc="/.gitbook/assets/arte.png"alt=""data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<imgsrc="/.gitbook/assets/arte.png"alt=""data-size="line">\
Learn & practice GCP Hacking: <imgsrc="/.gitbook/assets/grte.png"alt=""data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<imgsrc="/.gitbook/assets/grte.png"alt=""data-size="line">](https://training.hacktricks.xyz/courses/grte)
* **Dołącz do** 💬 [**grupy Discord**](https://discord.gg/hRep4RUj7f) lub [**grupy telegram**](https://t.me/peass) lub **śledź** nas na **Twitterze** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Podziel się trikami hackingowymi, przesyłając PR-y do** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repozytoriów github.
Aby zobaczyć, że wywołania systemowe są realizowane poprawnie, należy skompilować powyższy program, a wywołania systemowe powinny pojawić się w **strace ./PROGRAMA\_COMPILADO**
Podczas tworzenia shellcode'ów można zastosować trik. Pierwsza instrukcja to skok do wywołania. Wywołanie (call) odwołuje się do oryginalnego kodu i dodatkowo umieszcza EIP na stosie. Po instrukcji call umieściliśmy potrzebny ciąg, dzięki czemu z tym EIP możemy wskazać na ciąg i kontynuować wykonywanie kodu.
Składa się z małego kodu, który przeszukuje strony pamięci związane z procesem w poszukiwaniu shellcode tam przechowywanego (szuka jakiegoś podpisu umieszczonego w shellcode). Przydatne w przypadkach, gdy mamy tylko małą przestrzeń na wstrzyknięcie kodu.
Składają się z zaszyfrowanych shelli, które mają mały kod, który je deszyfruje i przeskakuje do niego, używając sztuczki Call-Pop, to byłby **przykład szyfrowania cezara**:
Patrząc na to, jak budowana jest stos nowego procesu w systemie Linux, można opracować exploit w taki sposób, aby program był uruchamiany w środowisku, którego jedyną zmienną byłby shellcode. Adres ten można obliczyć jako: addr = 0xbfffffff - 4 - strlen(NAZWA\_pełnego\_programu) - strlen(shellcode)
**sprintf** przenosi sformatowany ciąg **do****zmiennej.** Dlatego można nadużyć **formatowania** ciągu, aby spowodować **przepełnienie bufora w zmiennej**, do której kopiowana jest zawartość.\
Na przykład, ładunek `%.44xAAAA`**zapisze 44B+"AAAA" w zmiennej**, co może spowodować przepełnienie bufora.
**`atexit()`** to funkcja, do której **przekazywane są inne funkcje jako parametry.** Te **funkcje** będą **wykonywane** podczas wykonywania **`exit()`** lub **powrotu** z **main**.\
Jeśli możesz **zmodyfikować****adres** dowolnej z tych **funkcji**, aby wskazywał na shellcode, na przykład, zyskasz **kontrolę** nad **procesem**, ale obecnie jest to bardziej skomplikowane.\
Obecnie **adresy funkcji** do wykonania są **ukryte** za kilkoma strukturami, a ostatecznie adresy, na które wskazują, nie są adresami funkcji, ale są **szyfrowane za pomocą XOR** i przesunięć z **losowym kluczem**. Dlatego obecnie ten wektor ataku jest **niewiele użyteczny przynajmniej na x86** i **x64\_86**.\
Funkcja **szyfrująca** to **`PTR_MANGLE`**. **Inne architektury** takie jak m68k, mips32, mips64, aarch64, arm, hppa... **nie implementują funkcji szyfrującej**, ponieważ **zwraca to samo**, co otrzymała jako wejście. Tak więc te architektury byłyby atakowalne przez ten wektor.
**`Setjmp()`** pozwala na **zapisanie****kontekstu** (rejestrów)\
**`longjmp()`** pozwala na **przywrócenie****kontekstu**.\
**Zapisane rejestry** to: `EBX, ESI, EDI, ESP, EIP, EBP`\
Co się dzieje, to to, że EIP i ESP są przekazywane przez funkcję **`PTR_MANGLE`**, więc **architektury podatne na ten atak są takie same jak powyżej**.\
Są one przydatne do odzyskiwania błędów lub przerwań.\
Jednak, z tego co przeczytałem, inne rejestry nie są chronione, **więc jeśli w funkcji wywoływanej znajduje się `call ebx`, `call esi` lub `call edi`**, kontrola może być przejęta. Można również zmodyfikować EBP, aby zmodyfikować ESP.
Każdy obiekt klasy ma **VPtr**, który jest **wskaźnikiem** do tablicy swojej klasy. VPtr jest częścią nagłówka każdego obiektu, więc jeśli uda się **nadpisać****VPtr**, można go **zmodyfikować**, aby **wskazywał** na metodę zastępczą, tak aby wykonanie funkcji prowadziło do shellcode.
Przechwytywane są wywołania do niektórych niebezpiecznych funkcji przez inne bezpieczne. Nie jest to standaryzowane. (tylko dla x86, nie dla kompilacji z -fomit-frame-pointer, nie dla kompilacji statycznych, nie wszystkie funkcje podatne stają się bezpieczne, a LD\_PRELOAD nie działa w binariach z suid).
Polega na załadowaniu bibliotek współdzielonych od 0x00000000 do 0x00ffffff, aby zawsze był bajt 0x00. Jednak to naprawdę nie zatrzymuje prawie żadnego ataku, a tym bardziej w little endian.
Polega na przeprowadzeniu ROP w taki sposób, aby wywołać funkcję strcpy@plt (z plt) i wskazać na wpis w GOT oraz skopiować pierwszy bajt funkcji, którą chce się wywołać (system()). Następnie robi się to samo, wskazując na GOT+1 i kopiując 2. bajt system()… Na końcu wywołuje się adres zapisany w GOT, który będzie system().
W "size" są bity, aby wskazać: Czy poprzedni kawałek jest w użyciu, czy kawałek został przydzielony za pomocą mmap() i czy kawałek należy do głównej areny.
Jeśli podczas zwalniania kawałka którykolwiek z sąsiednich jest wolny, są one łączone za pomocą makra unlink() i nowy większy kawałek jest przekazywany do frontlink(), aby wstawił odpowiedni bin.
BK->fd = FD -> \*(\&shellcode + 8) = (&\_\_dtor\_end\_\_ - 12) —> To powoduje zapisanie 4 bajtów od 8. bajtu shellcode, więc pierwsza instrukcja shellcode musi być jmp, aby to przeskoczyć i przejść do nops, które prowadzą do reszty shellcode.
Po shellcode dodajemy wypełnienie, aż dotrzemy do pola prev\_size i size następnego kawałka. W tych miejscach umieszczamy 0xfffffff0 (w taki sposób, aby nadpisać prev\_size, aby miał bit wskazujący, że jest wolny) i “-4“(0xfffffffc) w size (aby, gdy sprawdzi w 3. kawałku, czy 2. był w rzeczywistości wolny, poszło do zmodyfikowanego prev\_size, które powie, że jest wolny) -> Tak więc, gdy free() zbada, pójdzie do size 3. kawałka, ale w rzeczywistości pójdzie do 2. - 4 i pomyśli, że 2. kawałek jest wolny. A następnie wywoła **unlink()**.
Podczas wywoływania unlink() użyje jako P->fd pierwszych danych 2. kawałka, więc tam wprowadzi adres, który chce nadpisać - 12 (ponieważ w FD->bk doda 12 do zapisanej w FD adresu). A w tym adresie wprowadzi drugi adres, który znajdzie w 2. kawałku, który nas interesuje, aby był adresem do shellcode (fałszywe P->bk).
**fake\_size = pack("\<I”, 0xfffffffc) #-4, aby myślał, że “size” 3. kawałka jest 4 bajty wstecz (wskazuje na prev\_size), ponieważ tam sprawdza, czy 2. kawałek jest wolny**
**got\_free = pack("\<I", 0x08048300 - 12) #Adres free() w plt-12 (będzie to adres, który zostanie nadpisany, aby uruchomić shellcode przy 2. wywołaniu free)**
**payload = "aaaabbbb" + shellcode + "b"\*(512-len(shellcode)-8) # Jak już powiedziano, ładunek zaczyna się od 8 bajtów wypełnienia, bo tak**
W ten sposób program pomyśli, że “a” jest wolne i w binie, więc wywoła unlink() w celu odłączenia go. Jednak, ponieważ nagłówek PREV\_SIZE wynosi -4, pomyśli, że kawałek “a” w rzeczywistości zaczyna się w b+4. To znaczy, wywoła unlink() na kawałku, który zaczyna się w b+4, więc w b+12 będzie wskaźnik “fd”, a w b+16 będzie wskaźnik “bk”.
Nazywa się frontlink, gdy zwalnia się coś i żaden z jego sąsiednich kawałków nie jest wolny, nie wywołuje się unlink(), lecz bezpośrednio wywołuje frontlink().
W ten sposób, osiągając nadpisanie w dwóch mallocach w sposób niekontrolowany i w jednym w sposób kontrolowany, ale tylko ten jeden jest zwalniany, możemy stworzyć exploit.
W przypadku chęci ponownego użycia jednego, zostanie przypisany bez problemów. W przypadku chęci użycia drugiego, zostanie przypisany ten sam obszar, przez co będziemy mieli wskaźniki “fd” i “bk” fałszywe z danymi, które zapisze poprzednia rezerwacja.
Wystarczy jedno wywołanie free(), aby spowodować wykonanie dowolnego kodu. Ważne jest, aby znaleźć drugi kawałek, który może być przepełniony przez wcześniejszy i zwolniony.
W \[1] sprawdza pole size bit NON\_MAIN\_ARENA, które można zmienić, aby sprawdzenie zwróciło true i wykonało heap\_for\_ptr(), które wykonuje and na “mem”, pozostawiając 2.5 najmniej znaczące bajty na 0 (w naszym przypadku z 0x0804a000 pozostawia 0x08000000) i uzyskuje dostęp do 0x08000000->ar\_ptr (jakby to był struct heap\_info)
W ten sposób, jeśli możemy kontrolować kawałek na przykład w 0x0804a000 i ma być zwolniony kawałek w **0x081002a0**, możemy dotrzeć do adresu 0x08100000 i zapisać, co chcemy, na przykład **0x0804a000**. Gdy ten drugi kawałek zostanie zwolniony, okaże się, że heap\_for\_ptr(ptr)->ar\_ptr zwraca to, co zapisaliśmy w 0x08100000 (ponieważ stosuje się do 0x081002a0 and, które widzieliśmy wcześniej, a z tego uzyskuje się wartość 4 pierwszych bajtów, ar\_ptr)
W adresie, w którym wyląduje adres drugiego kawałka z ostatnimi 5 zerami, należy zapisać adres do tego pierwszego kawałka, aby heap\_for\_ptr() myślał, że ar\_ptr jest na początku pierwszego kawałka i wyciągnie stamtąd av->bins\[2]
W ten sposób wywoła się \_int\_free(TROZO1, TROZO2) i wykona instrukcje, aby zapisać w \_\_DTOR\_END\_\_ adres prev\_size kawałka 2, który przeskoczy do shellcode.
Aby zastosować tę technikę, należy spełnić kilka dodatkowych wymagań, które nieco komplikują ładunek.
Ta technika nie jest już stosowana, ponieważ zastosowano prawie tę samą łatkę, co dla unlink. Porównuje się, czy nowe miejsce, na które wskazuje, również wskazuje na niego.
W ten sposób, jeśli umieścimy w “fb” adres funkcji w GOT, w tym adresie umieścimy adres do nadpisanego kawałka. Aby to zrobić, konieczne będzie, aby arena była blisko adresów dtors. Dokładniej, aby av->max\_fast znajdowało się w adresie, który zamierzamy nadpisać.
Dodatkowo musi być spełnione, że kawałek przylegający do zwolnionego musi być większy niż 8 -> Ponieważ powiedzieliśmy, że rozmiar zwolnionego kawałka wynosi 8, w tym fałszywym kawałku musimy umieścić tylko rozmiar większy niż 8 (ponieważ dodatkowo shellcode będzie w zwolnionym kawałku, na początku trzeba umieścić jmp, który trafi w nops).
Z powodu zer w \_DTOR\_END\_ i niewielkiej liczby adresów w GOT, żaden z adresów w tych sekcjach nie nadaje się do nadpisania, więc zobaczmy, jak zastosować fastbin do ataku na stos.
Dodatkowo musi być spełnione, że kawałek przylegający do zwolnionego musi być większy niż 8 -> Ponieważ powiedzieliśmy, że rozmiar zwolnionego kawałka wynosi 16, w tym fałszywym kawałku musimy umieścić tylko rozmiar większy niż 8 (ponieważ dodatkowo shellcode będzie w zwolnionym kawałku, na początku trzeba umieścić jmp, który trafi w nops, które są po polu size nowego fałszywego kawałka).
W tym przypadku staramy się mieć wskaźnik do malloc, który może być zmieniany przez atakującego (np. wskaźnik znajduje się na stosie pod potencjalnym przepełnieniem zmiennej).
W ten sposób moglibyśmy sprawić, że ten wskaźnik wskazywałby, gdziekolwiek byśmy chcieli. Jednak nie każde miejsce jest ważne, rozmiar fałszywego kawałka musi być mniejszy niż av->max\_fast i bardziej konkretnie równy rozmiarowi żądanym w przyszłym wywołaniu malloc()+8. Dlatego, jeśli wiemy, że po tym podatnym wskaźniku wywoływane jest malloc(40), rozmiar fałszywego kawałka musi wynosić 48.
Na przykład, jeśli program pytałby użytkownika o liczbę, moglibyśmy wprowadzić 48 i skierować wskaźnik malloc do następnych 4 bajtów (które mogłyby należeć do EBP z nadzieją, że 48 znajduje się za nim, jakby to była nagłówek size). Dodatkowo, adres ptr-4+48 musi spełniać kilka warunków (w tym przypadku ptr=EBP), to znaczy, 8 <ptr-4+48<av->system\_mem.
Jeśli to zostanie spełnione, gdy wywołane zostanie następne malloc, które powiedzieliśmy, że to malloc(40), zostanie przypisane jako adres adresu EBP. Jeśli atakujący również może kontrolować, co jest zapisywane w tym malloc, może nadpisać zarówno EBP, jak i EIP dowolnym adresem, który chce.
Myślę, że tak jest, ponieważ w ten sposób, gdy to zwolni free(), zapamięta, że w adresie, na który wskazuje EBP stosu, znajduje się kawałek o idealnym rozmiarze dla nowego malloc(), który chce być zarezerwowany, więc przypisuje ten adres.
Pierwsze, co się robi, to nadpisanie rozmiaru kawałka wilderness bardzo dużą wartością (0xffffffff), w ten sposób każda prośba o pamięć wystarczająco dużą będzie traktowana w \_int\_malloc() bez potrzeby rozszerzania heap.
Victim zbiera wartość adresu aktualnego kawałka wilderness (aktualny av->top), a remainder to dokładnie suma tego adresu i ilości bajtów żądanych przez malloc(). Dlatego, jeśli \&EIP-8 znajduje się w 0xbffff224, a av->top zawiera 0x080c2788, to ilość, którą musimy zarezerwować w kontrolowanym malloc, aby av->top wskazywał na $EIP-8 dla następnego malloc() wyniesie:
Ważne jest, aby rozmiar nowego kawałka wilderness był większy niż żądanie złożone przez ostatni malloc(). To znaczy, jeśli wilderness wskazuje na \&EIP-8, rozmiar znajdzie się dokładnie w polu EBP stosu.
Zwolnione kawałki są wprowadzane do binu w zależności od ich rozmiaru. Ale zanim zostaną wprowadzone, są przechowywane w unsorted bins. Kawałek jest zwalniany, nie jest od razu umieszczany w swoim binie, lecz pozostaje w unsorted bins. Następnie, jeśli zarezerwowany zostanie nowy kawałek, a poprzedni zwolniony może być użyty, zostanie zwrócony, ale jeśli zarezerwowany zostanie większy, zwolniony kawałek w unsorted bins zostanie umieszczony w odpowiednim binie.
Aby zarezerwować dwa malloc, tak aby do pierwszego można było przepełnić po tym, jak drugi został zwolniony i wprowadzony do swojego binu (to znaczy, aby zarezerwować malloc większy niż drugi kawałek przed przepełnieniem)
Celem jest następujące: jeśli możemy przepełnić heap, który ma pod sobą już zwolniony kawałek i w swoim binie, możemy zmienić jego wskaźnik bk. Jeśli zmienimy jego wskaźnik bk i ten kawałek stanie się pierwszym w liście bin, a zostanie zarezerwowany, bin zostanie oszukany i powie, że ostatni kawałek listy (następny do zaoferowania) znajduje się pod fałszywym adresem, który umieściliśmy (na stosie lub GOT, na przykład). W ten sposób, jeśli ponownie zarezerwowany zostanie inny kawałek, a atakujący ma do niego dostęp, otrzyma kawałek w pożądanej pozycji i będzie mógł w nim pisać.
Po zwolnieniu zmodyfikowanego kawałka konieczne jest, aby zarezerwowany został kawałek większy niż zwolniony, w ten sposób zmodyfikowany kawałek wyjdzie z unsorted bins i zostanie wprowadzony do swojego binu.
W ten sposób bin będzie musiał czekać na wywołanie malloc() wystarczająco wiele razy, aby ponownie użyć zmodyfikowanego binu i oszukać bin, sprawiając, że następny kawałek znajduje się pod fałszywym adresem. A następnie zostanie zwrócony kawałek, który nas interesuje.
Aby podatność została wykonana jak najszybciej, idealnie byłoby: rezerwacja podatnego kawałka, rezerwacja kawałka, który zostanie zmodyfikowany, zwolnienie tego kawałka, rezerwacja kawałka większego, który zostanie zmodyfikowany, zmodyfikowanie kawałka (podatność), rezerwacja kawałka o tym samym rozmiarze co zmodyfikowany i rezerwacja drugiego kawałka o tym samym rozmiarze, a ten będzie wskazywał na wybrany adres.
Aby chronić ten atak, zastosowano typowe sprawdzenie, że kawałek “nie” jest fałszywy: sprawdza się, czy bck->fd wskazuje na victim. To znaczy, w naszym przypadku, czy wskaźnik fd\* fałszywego kawałka wskazywanego na stosie wskazuje na victim. Aby obejść tę ochronę, atakujący powinien być w stanie w jakiś sposób (prawdopodobnie przez stos) zapisać w odpowiednim adresie adres victim. Aby w ten sposób wyglądało to jak prawdziwy kawałek.
Atak jest jak poprzedni, to znaczy, trzeba zmodyfikować wskaźnik bk i potrzebne są wszystkie te wywołania malloc(), ale dodatkowo trzeba zmodyfikować rozmiar zmodyfikowanego kawałka w taki sposób, aby ten rozmiar - nb był <MINSIZE.
Zasadniczo polega na rezerwowaniu całej możliwej pamięci dla heapów i wypełnianiu ich poduszką nops zakończoną shellcode. Dodatkowo, jako poduszkę używa się 0x0c. Ponieważ spróbuje się przeskoczyć do adresu 0x0c0c0c0c, a więc, jeśli nadpisze się jakiś wskaźnik, do którego się wezwie, z tą poduszką, przeskoczy się tam. Zasadniczo taktyka polega na rezerwowaniu jak najwięcej, aby zobaczyć, czy nadpisze się jakiś wskaźnik i przeskoczy do 0x0c0c0c0c, mając nadzieję, że tam będą nops.
Polega na tym, aby za pomocą rezerwacji i zwolnień zasadzić pamięć w taki sposób, aby między kawałkami zarezerwowanymi znajdowały się kawałki wolne. Bufor do przepełnienia znajdzie się w jednym z jajek.
Ucz się i ćwicz Hacking AWS:<imgsrc="/.gitbook/assets/arte.png"alt=""data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<imgsrc="/.gitbook/assets/arte.png"alt=""data-size="line">\
Ucz się i ćwicz Hacking GCP: <imgsrc="/.gitbook/assets/grte.png"alt=""data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<imgsrc="/.gitbook/assets/grte.png"alt=""data-size="line">](https://training.hacktricks.xyz/courses/grte)
* **Dołącz do** 💬 [**grupy Discord**](https://discord.gg/hRep4RUj7f) lub [**grupy telegram**](https://t.me/peass) lub **śledź** nas na **Twitterze** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Podziel się sztuczkami hackingowymi, przesyłając PR do** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) repozytoriów github.