No-exec / NX
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Basic Information
The No-Execute (NX) bit, also known as Execute Disable (XD) in Intel terminology, is a hardware-based security feature designed to mitigate the effects of buffer overflow attacks. When implemented and enabled, it distinguishes between memory regions that are intended for executable code and those meant for data, such as the stack and heap. The core idea is to prevent an attacker from executing malicious code through buffer overflow vulnerabilities by putting the malicious code in the stack for example and directing the execution flow to it.
Modern operating systems enforce NX through the page table attributes that back the ELF program headers. For example, the PT_GNU_STACK header combined with the GNU_PROPERTY_X86_FEATURE_1_SHSTK or GNU_PROPERTY_X86_FEATURE_1_IBT properties let the loader know whether the stack should be RW or RWX. When NX is enabled and the binary was linked with a non-executable stack (-z noexecstack), any attempt to pivot execution into attacker-controlled data pages (stack, heap, mmap’ed buffers, etc.) will raise a fault unless those pages were explicitly marked as executable.
Detecting NX quickly
checksec --file ./vulnwill displayNX enabledorNX disabledbased on theGNU_STACKprogram header.readelf -W -l ./vuln | grep GNU_STACKexposes the stack permissions; the presence of anEflag indicates that the stack is executable. Example:
$ readelf -W -l ./vuln | grep GNU_STACK
GNU_STACK 0x000000 0x000000 0x000000 0x000000 0x000000 RW 0x10
execstack -q ./vuln(fromprelink) is handy when auditing large collections of binaries because it printsXfor binaries that still have an executable stack.- At runtime,
/proc/<pid>/mapswill show whether an allocation isrwx,rw-,r-x, etc., which is useful when verifying JIT engines or custom allocators.
Bypasses
Code-reuse primitives
It’s possible to use techniques such as ROP to bypass this protection by executing chunks of executable code already present in the binary. Typical chains include:
- Ret2libc
- Ret2syscall
- Ret2dlresolve when the binary does not import
system/execve - Ret2csu or Ret2vdso to synthesize syscalls
- Ret2… — any dispatcher that lets you stitch controlled register state with existing executable code to invoke syscalls or library gadgets.
The workflow is usually: (1) leak a code or libc pointer through an info leak, (2) resolve function bases, and (3) craft a chain that never needs attacker-controlled executable bytes.
Sigreturn Oriented Programming (SROP)
SROP builds a fake sigframe on a writable page and pivots execution to sys_rt_sigreturn (or the relevant ABI equivalent). The kernel then “restores” the crafted context, instantly granting full control over all general-purpose registers, rip, and eflags. Recent CTF challenges (e.g., the Hostel task in n00bzCTF 2023) show how SROP chains first invoke mprotect to flip the stack to RWX, then reuse the same stack for shellcode, effectively bypassing NX even when only a single syscall; ret gadget is available. Check the dedicated SROP page for more architecture-specific tricks.
Ret2mprotect / ret2syscall to flip permissions
If you can call mprotect, pkey_mprotect, or even dlopen, you can legitimately request an executable mapping before running shellcode. A small pwntools skeleton looks like:
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())})
The same idea applies to ret2syscall chains that set rax=__NR_mprotect, point rdi to a mmap/.bss page, store the desired length in rsi, and set rdx=7 (PROT_RWX). Once a RWX region exists, execution can safely jump into attacker-controlled bytes.
RWX primitives from JIT engines and kernels
JIT engines, interpreters, GPU drivers, and kernel subsystems that dynamically emit code are a common way to regain executable memory even under strict NX policies. The 2024 Linux kernel vulnerability CVE-2024-42067 showed that failures in set_memory_rox() left eBPF JIT pages writable and executable, letting attackers spray gadgets or entire shellcode blobs inside the kernel despite NX/W^X expectations. Exploits that gain control over a JIT compiler (BPF, JavaScript, Lua, etc.) can therefore arrange for their payload to live in those RWX arenas and only need a single function pointer overwrite to jump into them.
Non-return code reuse (JOP/COP)
If ret instructions are hardened (e.g., CET/IBT) or the binary lacks expressive ret gadgets, pivot to Jump-Oriented Programming (JOP) or Call-Oriented Programming (COP). These techniques build dispatchers that use jmp [reg] or call [reg] sequences found in the binary or loaded libraries. They still respect NX because they reuse existing executable code, but they sidestep mitigations that specifically watch for large chains of ret instructions.
References
Tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
HackTricks

