11

I once heard the NX bit was a panacea, then that it was not. One detail I've wondered about though: Does the NX (no execute) bit protect against code inserted into the stack and executed there? It seems to me that the stack, because it's not the heap, might not be protected by NX usually. THanks.

Franch
  • 153
  • 1
  • 1
  • 4
  • There are 2 really good answers already, so here's the TLDR: NX is a generic flag that can be applied to any memory page, stack or otherwise. Whether it's actually enabled is up to the OS, and possibly the application. – Mark E. Haase Dec 31 '13 at 00:22

2 Answers2

20

The NX bit is a feature of the Memory Management Unit of some CPU (including recent enough x86). It allows to mark each memory page as being "allowed" or "disallowed" for code execution. The MMU is under control of the kernel; the kernel code decides which pages get the execution privilege and which do not. Therefore, whether the stack space is protected against execution depends on the OS. It also depends on a lot of other things:

  • Though the kernel may apply default values for the page properties, application can request changes. E.g. on Linux systems, the mprotect() system call can be used to enable or disable execution on any page that is under control of the calling process. In particular, any application which contains a JIT compiler (e.g. Web browsers, for efficient Javascript support) must play with mprotect(), because it must generate code (i.e. write bytes into some memory) and then execute the said code.

  • A mono-threaded application gets its stack in a dedicated area that the kernel knows of; in particular, the stack pages are automatically allocated upon first usage. In a multi-threaded application, things change: each thread has its own stack, which is, from the point of view of the kernel, heap-allocated. Depending on the OS, there may or may not be special support from the kernel for thread stacks.

  • GCC in particular supports a C language extension called nested functions. Because of the semantics of these functions, the compiled code for a nested function must dynamically produce some executable code on the stack; this is called a trampoline. See this page for some details. The bottom-line is that, for a trampoline to actually work, the stack (or at least the page in which the trampoline resides) must be marked executable.

It so happens that in the ELF file format, which is the format of executable files and DLL on Linux, there is a field which allows to specify whether the stack should be executable or not. When GCC compiles some code which contains a nested function, it sets this flag in the produced binary.

I have made some tests on a fairly recent Linux (Ubuntu 13.10, on 64-bit x86, with NX bit activated). Whether a given page is executable or not can be inferred from the contents of the special file /proc/$$/maps where $$ is the process ID. These tests have shown the following:

  • By default, the main stack is not executable. The NX bit is set for the stack pages.
  • If code contains a nested function, the executable is marked with the "executable stack" flag and, indeed, the main stack is now executable.
  • Thread stacks are created with the same "executable status" as the main stack.
  • If a "normal" executable, with its main stack non-executable, dynamically loads a DLL (with dlopen()) which contains code for a nested function, then the main stack is automatically switched to executable status.
  • If a normal multi-threaded executable, with its main stack and all its thread stacks non-executable, dynamically loads a DLL which contains code for the nested function, then the main stack and all the thread stacks are en masse promoted to executable status. Note that this implies that on this Linux system, "something" (probably the kernel, I have not checked) is aware of all the current thread stacks, and can change their mapping rights dynamically.

From all of this we conclude that on at least some recent versions of Linux, the stacks (main and threads) will usually be marked as non-executable (i.e. NX bit set). However, if the executable code of the application, or some executable code somewhere in a DLL loaded by the application, contains a nested function or otherwise advertises a need for an executable stack, then all stacks in the application will be marked as executable (i.e. NX bit not set). In other words, a single loadable plugin or extension for an application may deactivate NX stack protection for all threads of the applications, simply by using a rarely used but legitimate and documented feature of GCC.

There is no need to panic, though, because, as @tylerl points out, protection afforded by the NX bit is not that great. It will make some exploits more awkward for the least competent of attackers; but good attackers will not be impeded. Also, all this talk about NX is about trying to contain damage once a buffer overflow or use-after-free has occurred, and that's arguably a bit late to react.

Tom Leek
  • 168,808
  • 28
  • 337
  • 475
  • 3
    "Also, all this talk about NX is about trying to contain damage once a buffer overflow or use-after-free has occurred, and that's arguably a bit late to react." -- The same argument can be made about seatbelts and airbags, and nobody sane claims that this means they're not a good idea. Other than that, good answer. – Shadur Dec 31 '13 at 12:30
  • 1
    The argument means that nothing of the nature of the NX bit can be thought as absolute security; not because it can be bypassed, but because it has late action. Similarly, the best airbag in the World will save your life but won't prevent your car from being ruined. Accident _prevention_ measures should thus be favoured. – Tom Leek Dec 31 '13 at 21:20
  • Actually until recently, web browsers did not use `mprotect()` for JIT. Rather, they created an already RWX mapping using `mmap()`. Some more recent browsers, in order to be compatible with OpenBSD and iOS' W^X security feature, will use `mmap()` to create a RW page, populate it with bytecode, then `mprotect()` to make it RX. AFAIK, Firefox for example did this only in version 56 (?). – guest Nov 19 '17 at 03:53
  • 1
    As for NX being absolute security, it used to be thought of that way before the discovery of ROP (ret2lib) attacks. – guest Nov 19 '17 at 07:05
6

By marking the stack as non-execute, you effectively prevent code inserted into the stack from running. You're not protecting the stack from modification; rather, you're causing a hard crash when the code attempts to jump to a position in the NX-marked stack.

The workaround is to not attempt to execute code on the stack. Instead of setting the return position to a spot in the stack, you set it to the location of a system function such as exec(), setting the spots you overwrote on the stack to contain arguments to the exec() system call instead of executable machine code. In such a scenario, the attacker can run a system function with the parameters of his choosing, which is sufficient, for example, to execute another program.

NX therefore provides some level of protection, but not tons. It somewhat limits the flexibility of what an attacker can do with the initial exploit, but given he can leverage that exploit to execute a shell, there's not a lot of safety there.

The next layer in defense is ASLR, which randomizes the location of system functions such as exec(). This way, when he attempts to execute his shell, he won't know what address to load into the return location.

Attacks against ALSR are typically brute force; run your exploit multiple times, trying different return addresses each time. This means that ALSR is significantly less effective on 32-bit system than on 64-bit because of the limited range of randomization, allowing for relatively fast brute-force attacks.

ASLR combined with NX offers some degree of protection, but nothing is absolute.

tylerl
  • 82,225
  • 25
  • 148
  • 226
  • Brute force against ASLR won't work because even if you find the address, on next boot the address will be different. That's why you will always look for a pointer on a predictable location which is pointing to one of these functions or libraries. This is known as information leak. – void_in Dec 30 '13 at 06:16
  • @void_in a brute-force attack would be strictly on-line, not a pre-computed attack. – tylerl Dec 30 '13 at 07:43
  • Depending on the particular binary it may even be possible to disable the hardware NX protection. See this question which is fairly close to yours: http://security.stackexchange.com/questions/20497/stack-overflows-defeating-canaries-aslr-dep-nx – David Hoelzer Dec 30 '13 at 23:03
  • @void_in, ASLR can be defeated under certain conditions e.g., Blind ROP – ultrajohn Sep 08 '19 at 12:24