AD3002: DoNotMarkStackAsExecutable¶
Summary¶
| Property | Value |
|---|---|
| ID | AD3002 |
| Name | DoNotMarkStackAsExecutable |
| Category | Security |
| Severity | Error |
| Applies to | ELF (Linux/Unix) |
Description¶
This checks if a binary has an executable stack. An executable stack allows attackers to redirect code flow into stack memory, which is an easy place for an attacker to store shellcode.
Ensure you are compiling with -z noexecstack to mark the stack as non-executable.
How It Works¶
The rule examines the PT_GNU_STACK program header in the ELF file. This header specifies stack permissions:
- No PT_GNU_STACK: Stack is executable (legacy default)
- PT_GNU_STACK with PF_X: Stack explicitly marked executable
- PT_GNU_STACK without PF_X: Stack is non-executable (correct)
Why This Matters¶
An executable stack is the oldest and most direct exploitation primitive. It allows attackers to place their code on the stack and jump directly to itโthe simplest possible path to code execution.
Historical Context¶
Before NX/DEP, the stack was executable by default:
1988: Morris Worm uses stack-based shellcode
...
1996: Aleph One's "Smashing the Stack for Fun and Profit"
(Classic shellcode-on-stack technique)
...
2004: AMD64 introduces NX bit
2005: Intel EM64T adds XD bit
Today: Executable stack is a critical vulnerability
The Classic Stack Smash¶
With executable stack, exploitation is trivial:
void vulnerable(char *input) {
char buffer[64];
strcpy(buffer, input); // Overflow!
}
// Attack payload:
// [SHELLCODE] [PADDING] [RETURN_ADDR โ buffer]
// โ
// Jump to stack, execute shellcode
Without executable stack, this direct approach fails.
Attack Difficulty Comparison¶
| Stack Permission | Exploitation Complexity |
|---|---|
| Executable | Simple shellcode on stack |
| Non-executable | Must use ROP/JOP techniques |
| Non-executable + CFG | ROP becomes harder |
| Non-executable + CET | ROP largely defeated |
Hardware Enforcement¶
Non-executable stack uses CPU features:
| Platform | Feature | Bit in Page Table |
|---|---|---|
| AMD64 | NX (No-Execute) | Bit 63 |
| Intel | XD (Execute Disable) | Bit 63 |
| ARM | XN (Execute Never) | Varies by version |
Hardware enforcement means: - No performance overhead - Cannot be bypassed by application code - Enforced on every instruction fetch
Common Causes of Executable Stack¶
| Cause | Solution |
|---|---|
| Nested functions (GCC extension) | Avoid or use -ftrampolines |
Assembly without .note.GNU-stack |
Add proper section |
| Old compiler/linker defaults | Update toolchain |
| Deliberate for JIT | Use mprotect carefully |
ELF Stack Permission Indicator¶
PT_GNU_STACK segment flags:
PF_R | PF_W = Non-executable (correct)
PF_R | PF_W | PF_X = Executable (vulnerable)
Missing segment = Executable (legacy default)
Defense in Depth Role¶
Non-executable stack is foundational:
First line: Non-executable stack (blocks direct shellcode)
Second line: ASLR (randomizes addresses)
Third line: Stack canaries (detects overflow)
Fourth line: CFG/CET (blocks control flow hijacking)
- Shellcode prevention: Can't execute code placed on stack
- Buffer overflow protection: Classic exploits become harder
- NX/DEP equivalent: Hardware-enforced protection
- Defense in depth: Complements other mitigations
Resolution¶
GCC/Clang¶
Add the noexecstack linker flag:
For Assembly Files¶
Assembly files can mark stack as executable. Fix with:
Or add to assembly file:
CMake¶
add_link_options(-Wl,-z,noexecstack)
# For specific target
target_link_options(myapp PRIVATE -Wl,-z,noexecstack)
Makefile¶
When to Suppress¶
This rule should rarely be suppressed. Exceptions might include:
- Trampoline code: Some nested functions need executable stack
- JIT compilers: May manage their own executable memory
- Legacy compatibility: Very old code patterns
Caveats¶
- GCC nested functions may require executable stack (use alternatives)
- Some assembly files implicitly mark stack executable
- Legacy binaries may have this issue
Common Causes¶
- Assembly without
.note.GNU-stack - GCC trampolines for nested functions
- Old object files linked in
- Missing
-z noexecstackflag
Fixing Assembly Files¶
Add this section to mark stack as non-executable:
# AT&T syntax
.section .note.GNU-stack,"",@progbits
# Intel syntax
section .note.GNU-stack noalloc noexec nowrite progbits