Эта страница документирует Урок 8 курса: исследование того, как рандомизация раскладки адресного пространства (Address Space Layout Randomization, ASLR) влияет на запущенные процессы и как позиционно-независимые исполняемые файлы (Position Independent Executables, PIE) взаимодействуют с ASLR. В уроке используются четыре диагностических бинарника, делающие рандомизацию адресов непосредственно наблюдаемой.
Данная страница посвящена пониманию поведения ASLR, а не его обходу. Техники обхода, которые строятся на этом знании, описаны в Bypassing ASLR/NX with Ret2PLT и Bypassing ASLR/NX with GOT Overwrite. О предпосылках существования ASLR и NX см. Linux Binary Protections.
Соответствующие исходные файлы:
ASLR управляется параметром ядра, доступным через виртуальную файловую систему procfs по пути /proc/sys/kernel/randomize_va_space.
| Значение | Эффект |
|---|---|
0 |
ASLR выключен. Все сегменты (стек, куча, библиотеки, исполняемый файл) загружаются по фиксированным адресам при каждом запуске. |
1 |
Частичный ASLR. Стек, VDSO и разделяемые библиотеки рандомизируются; куча — нет. |
2 |
Полный ASLR. Стек, куча, разделяемые библиотеки и (если PIE) сам исполняемый файл — всё рандомизируется. |
Упражнения урока используют значения 0 и 2:
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space # disable
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space # full ASLRlessons/8_aslr/lessonplan.md33-36
Все четыре бинарника компилируются из одного файла исходного кода src/1_reveal_addresses.c, но с разными флагами компилятора. Makefile определяет, как собирается каждый из них:
| Бинарник | Архитектура | PIE | Флаги компилятора |
|---|---|---|---|
1_reveal_addresses |
i386 (32-бит) | Без PIE | -m32 -ldl |
2_reveal_addresses64 |
x86-64 (64-бит) | Без PIE | -ldl |
3_reveal_addresses_pie |
i386 (32-бит) | PIE включён | -m32 -ldl -pie |
4_reveal_addresses64_pie |
x86-64 (64-бит) | PIE включён | -ldl -pie -fPIC |
Ключевые флаги компиляции, управляющие PIE:
- Без PIE: никаких особых флагов. Линковщик назначает фиксированные виртуальные адреса сегментам исполняемого файла на этапе линковки.
-pie: указывает линковщику создать позиционно-независимый исполняемый файл.-fPIC: указывает GCC генерировать позиционно-независимый код (требуется для 64-битных PIE-сборок).
Каждый бинарник во время выполнения делает следующее (из lessons/8_aslr/lessonplan.md7-25):
- Вызывает
getpid(), чтобы получить свой собственный PID. - Запускает
cat /proc/<pid>/mapsчерезsystem(), выводя полную раскладку памяти. - Использует
dlsym(RTLD_NEXT, "system"), чтобы напечатать адресsystemв libc во время выполнения.
Это даёт прямое, читаемое человеком представление того, куда каждый сегмент фактически отображён при каждом запуске.
Диаграмма: раскладка адресов PIE vs Non-PIE
Источники: lessons/8_aslr/lessonplan.md176-179 lessons/8_aslr/Makefile1-8
Ключевое различие:
- В не-PIE бинарнике собственные сегменты text/data исполняемого файла находятся по адресам, зафиксированным на этапе линковки (например,
0x08048000для 32-битного бинарника). Включение ASLR рандомизирует библиотеки и стек, но не исполняемый файл. - В PIE бинарнике динамический линковщик обращается с исполняемым файлом как с разделяемым объектом и помещает его по случайному базовому адресу. При уровне ASLR 2 каждая область, включая сам исполняемый файл, рандомизируется при каждом запуске.
При randomize_va_space=0 повторные запуски любого из бинарников показывают одни и те же адреса каждый раз.
Пример из 1_reveal_addresses (32-бит, без PIE), lessons/8_aslr/lessonplan.md42-84:
| Область | Фиксированный адрес (ASLR выключен) |
|---|---|
| Сегмент text исполняемого файла | 0x08048000 |
| Куча | 0x0804b000 |
libc-2.23.so (text) |
0xf7e10000 |
| Стек | 0xfffdd000 |
system в libc |
0xf7e4ada0 |
Пример из 4_reveal_addresses64_pie (64-бит, PIE), lessons/8_aslr/lessonplan.md87-133:
| Область | Фиксированный адрес (ASLR выключен) |
|---|---|
| Сегмент text исполняемого файла | 0x555555554000 |
| Куча | 0x555555756000 |
libc-2.23.so (text) |
0x7ffff780a000 |
| Стек | 0x7ffffffde000 |
system в libc |
0x7ffff784f390 |
Даже при включённом PIE адреса детерминированы, когда ASLR выключен. Адрес загрузки PIE 0x555555554000 — это фиксированное значение по умолчанию, выбираемое динамическим линковщиком в отсутствие рандомизации.
Диаграмма: что рандомизирует ASLR в зависимости от PIE
Источники: lessons/8_aslr/lessonplan.md135-179
При включённом ASLR:
- Не-PIE бинарники (
1_reveal_addresses,2_reveal_addresses64): собственные сегменты исполняемого файла остаются по своим фиксированным адресам линковки. Рандомизируются только библиотеки, стек и куча. Атакующий, знающий, что бинарник не-PIE, всё ещё имеет надёжные адреса ROP-гаджетов внутри исполняемого файла. - PIE бинарники (
3_reveal_addresses_pie,4_reveal_addresses64_pie): каждая область, включая сам исполняемый файл, рандомизируется. Печатаемый адресsystemменяется при каждом вызове.
checksec (из pwntools/peda) читает метаданные ELF, чтобы сообщить, был ли бинарник скомпилирован с PIE. В уроке показан вывод checksec для всех четырёх бинарников:
lessons/8_aslr/lessonplan.md148-174
Диаграмма: соответствие вывода checksec файлам бинарников
Источники: lessons/8_aslr/lessonplan.md148-174
Сводка результатов checksec:
| Бинарник | Архитектура | PIE | NX | Стек-канарейка |
|---|---|---|---|---|
1_reveal_addresses |
i386-32-little | No PIE | Включён | Canary found |
2_reveal_addresses64 |
amd64-64-little | No PIE | Включён | Canary found |
3_reveal_addresses_pie |
i386-32-little | PIE enabled | Включён | Canary found |
4_reveal_addresses64_pie |
amd64-64-little | PIE enabled | Включён | Canary found |
Во всех четырёх бинарниках включены NX и стек-канарейки. Единственное различие в наборе — архитектура (32 или 64 бита) и статус PIE.
Наблюдения из этого урока напрямую определяют стратегии обхода, рассматриваемые в последующих уроках:
| Сценарий | Адреса исполняемого файла предсказуемы? | Адреса библиотек предсказуемы? | Подход |
|---|---|---|---|
| ASLR выключен, не-PIE | Да | Да | Прямой ret2libc (Урок 7) |
| ASLR включен, не-PIE | Да | Нет | Ret2PLT (Урок 9), перезапись GOT (Урок 10) |
| ASLR включен, PIE | Нет | Нет | Требуется утечка памяти (Урок 12, 13) |
Ключевое наблюдение: даже при полностью включённом ASLR (randomize_va_space=2) собственная секция кода не-PIE бинарника (а значит, и его заглушки PLT и ROP-гаджеты) находится по фиксированному, известному адресу. Это фундамент техники Ret2PLT, описанной в Bypassing ASLR/NX with Ret2PLT.
Источники: lessons/8_aslr/lessonplan.md1-179 lessons/8_aslr/Makefile1-8