No-exec / NX

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

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

Біт No-Execute (NX), також відомий як Execute Disable (XD) в термінології Intel, — це апаратна функція безпеки, призначена для пом’якшити наслідків атак типу buffer overflow. При реалізації та ввімкненні він розрізняє області пам’яті, призначені для executable code, і ті, що призначені для data, такі як stack і heap. Головна ідея — перешкодити атакувальнику виконати шкідливий код через вразливості buffer overflow, наприклад розмістивши шкідливий код у stack і спрямувавши туди потік виконання.

Сучасні операційні системи примусово застосовують NX через атрибути page table, що підтримують заголовки ELF. Наприклад, заголовок PT_GNU_STACK у поєднанні з властивостями GNU_PROPERTY_X86_FEATURE_1_SHSTK або GNU_PROPERTY_X86_FEATURE_1_IBT повідомляє loader, чи має stack бути RW чи RWX. Коли NX увімкнено і бінарник зібрано з невиконуваним стеком (-z noexecstack), будь-яка спроба перенаправити виконання в керовані атакувальником сторінки даних (stack, heap, mmap’ed buffers тощо) призведе до помилки, якщо ці сторінки явно не були позначені як виконувані.

Швидке виявлення NX

  • checksec --file ./vuln покаже NX enabled або NX disabled на основі заголовка GNU_STACK.
  • readelf -W -l ./vuln | grep GNU_STACK показує дозволи для stack; наявність прапора E вказує, що stack є виконуваним. Приклад:
$ readelf -W -l ./vuln | grep GNU_STACK
GNU_STACK      0x000000 0x000000 0x000000 0x000000 0x000000 RW  0x10
  • execstack -q ./vuln (from prelink) зручно при перевірці великих колекцій бінарників, оскільки він виводить X для бінарників, що все ще мають executable stack.
  • Під час виконання /proc/<pid>/maps покаже, чи виділення є rwx, rw-, r-x тощо, що корисно при перевірці JIT engines або custom allocators.

Обходи

Code-reuse primitives

Можна використати техніки на кшталт ROP для обходу цього захисту шляхом виконання шматків executable code, які вже присутні в бінарнику. Типові ланцюжки включають:

  • Ret2libc
  • Ret2syscall
  • Ret2dlresolve коли бінарник не імпортує system/execve
  • Ret2csu або Ret2vdso для синтезу syscalls
  • Ret2… — будь-який диспетчер, що дозволяє зшивати контрольований стан регістрів з наявним executable code для виклику syscalls або library gadgets.

Зазвичай робочий процес такий: (1) leak code або libc pointer через info leak, (2) визначити bases функцій, та (3) скласти chain, який ніколи не потребує attacker-controlled executable bytes.

Sigreturn Oriented Programming (SROP)

SROP будує фейковий sigframe на writable сторінці і переносить виконання до sys_rt_sigreturn (або відповідного ABI еквівалента). Kernel потім «відновлює» зконструйований контекст, миттєво надаючи повний контроль над усіма general-purpose registers, rip та eflags. Недавні CTF задачі (наприклад, завдання Hostel у n00bzCTF 2023) показують, як SROP-ланцюжки спочатку викликають mprotect, щоб переключити стек у RWX, а потім повторно використовують той самий стек для shellcode, ефективно обходячи NX навіть коли доступний лише один syscall; ret gadget. Див. присвячену SROP page для більш архітектурно-специфічних трюків.

Ret2mprotect / ret2syscall to flip permissions

Якщо ви можете викликати mprotect, pkey_mprotect або навіть dlopen, ви можете легітимно запросити executable mapping перед запуском shellcode. Маленький pwntools skeleton виглядає так:

from pwn import *
elf = ELF("./vuln")
rop = ROP(elf)
rop.mprotect(elf.bss(), 0x1000, 7)
payload = flat({offset: rop.chain(), offset+len(rop.chain()): asm(shellcraft.sh())})

Та сама ідея застосовується до ланцюгів ret2syscall, які встановлюють rax=__NR_mprotect, вказують rdi на сторінку mmap/.bss, записують бажану довжину в rsi і встановлюють rdx=7 (PROT_RWX). Як тільки існує RWX-область, виконання може безпечно стрибнути в байти, контрольовані атакуючим.

RWX-примітиви з JIT-двигунів та ядер

JIT-двигуни, інтерпретатори, драйвери GPU та підсистеми ядра, які динамічно генерують код, є поширеним способом відновити виконувану пам’ять навіть при суворих NX-політиках. Уразливість ядра Linux 2024 року CVE-2024-42067 показала, що збої в set_memory_rox() залишали сторінки eBPF JIT одночасно записуваними і виконуваними, дозволяючи атакуючим розпилювати gadgets або цілі shellcode blobs всередині ядра, незважаючи на очікування NX/W^X. Експлойти, які отримують контроль над JIT-компілятором (BPF, JavaScript, Lua тощо), можуть помістити свій payload у ці RWX-області й потребують лише одного перезапису вказівника на функцію, щоб стрибнути в них.

Повторне використання коду без return (JOP/COP)

Якщо інструкції ret захищені (наприклад, CET/IBT) або в бінарі бракує виразних ret gadgets, переходьте до Jump-Oriented Programming (JOP) або Call-Oriented Programming (COP). Ці техніки будують диспетчери, які використовують послідовності jmp [reg] або call [reg], знайдені в бінарному файлі або завантажених бібліотеках. Вони все ще дотримуються NX, оскільки повторно використовують існуючий виконуваний код, але обходять заходи пом’якшення, що спеціально відстежують великі ланцюги інструкцій ret.

ROP & JOP

Посилання

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