AD2021: DoNotMarkWritableSectionsAsExecutable¶
Summary¶
| Property | Value |
|---|---|
| ID | AD2021 |
| Name | DoNotMarkWritableSectionsAsExecutable |
| Category | Security |
| Severity | Error |
| Applies to | PE (Windows) |
Description¶
PE sections should not be marked as both writable and executable. This condition makes it easier for an attacker to exploit memory corruption vulnerabilities, as it may provide an attacker executable location(s) to inject shellcode.
To resolve this issue, configure your tools to not emit memory sections that are writable and executable. Be sure to disable incremental linking in release builds, as this feature creates a writable and executable section named .textbss.
How It Works¶
The rule examines each section in the PE file and checks for the combination of:
IMAGE_SCN_MEM_WRITE(0x80000000)IMAGE_SCN_MEM_EXECUTE(0x20000000)
Sections should not have both flags set (W^X principle: Write XOR Execute).
Why This Matters¶
Writable and executable memory (W+X) is the most exploitable memory configuration possible. It allows attackers to write their code and immediately execute it—the simplest possible exploitation primitive.
The W^X Principle¶
W^X (Write XOR Execute) is a fundamental security principle:
Secure: Memory is Writable XOR Executable (one or the other, never both)
Insecure: Memory is Writable AND Executable (attacker's dream)
| Configuration | Use Case | Exploitability |
|---|---|---|
| R-- | Read-only data | Not exploitable |
| RW- | Normal data | Data-only attacks |
| R-X | Normal code | Must use existing code |
| RWX | JIT, .textbss | Direct shellcode injection |
The .textbss Problem¶
Incremental linking creates a .textbss section that is both writable and executable:
Incremental Linking:
- Reserves space between functions for growth
- Uses .textbss section for this space
- .textbss must be writable (for linker) and executable (it's code space)
- Result: Perfect shellcode target
This is why release builds should always disable incremental linking.
Exploitation Made Trivial¶
With W+X memory available:
1. Find write primitive (overflow, format string, etc.)
2. Write shellcode to W+X section
3. Redirect execution there
4. Game over
Without W+X:
1. Find write primitive
2. Can't write executable code
3. Must use ROP/JOP (complex, fragile)
4. Eventually call VirtualProtect or equivalent
5. Then finally execute payload
Historical Attacks¶
W+X memory has been targeted in major attacks:
- Heap sprays: Filled W+X regions with shellcode
- JIT sprays: Abused JIT compilers creating W+X memory
- Browser exploits: Targeted plugins with W+X memory
Why Incremental Linking Uses W+X¶
Incremental linking optimizes development builds:
- First build: Linker leaves gaps between functions
- Subsequent builds: Modified functions fit in gaps
- Benefit: Faster linking during development
- Cost: .textbss must be W+X
For release builds, full linking is always preferred.
Measuring the Impact¶
| Build Type | W+X Sections | Exploitation |
|---|---|---|
| Release (no incremental) | None | Hard |
| Debug (incremental) | .textbss | Trivial |
| Release (incremental—wrong!) | .textbss | Trivial |
Defense in Depth Breakdown¶
W+X memory undermines multiple defenses:
| Mitigation | Effect of W+X |
|---|---|
| DEP | Partially bypassed (W+X region is executable) |
| CFG | Less effective (more valid targets) |
| ASLR | Less important (write anywhere, execute there) |
Resolution¶
Disable Incremental Linking¶
The most common cause is incremental linking creating .textbss:
Project Properties¶
- Open Project Properties
- Navigate to Linker → General
- Set "Enable Incremental Linking" to "No (/INCREMENTAL:NO)"
Release Configuration¶
Ensure release builds don't use incremental linking:
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
CMake¶
if(MSVC)
# Disable incremental linking for release
set(CMAKE_EXE_LINKER_FLAGS_RELEASE
"${CMAKE_EXE_LINKER_FLAGS_RELEASE} /INCREMENTAL:NO")
endif()
Check Custom Section Attributes¶
Remove W+X from custom sections:
# Bad
link.exe /SECTION:mysect,ERW ...
# Good - either executable OR writable
link.exe /SECTION:mysect,ER ...
# OR
link.exe /SECTION:mysect,RW ...
When to Suppress¶
This rule may be suppressed for:
- JIT compilers: Must manage W→X transitions carefully
- Packers/Unpackers: Self-modifying code (security risk)
- Debugger stubs: Some debugging scenarios
Caveats¶
.textbssis the most common offender (incremental linking)- Some obfuscators create W+X sections intentionally
- JIT compilers need careful W→X memory management
Proper JIT Memory Management¶
If you need to write and then execute code:
// 1. Allocate writable
void* mem = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
// 2. Write code
memcpy(mem, code, size);
// 3. Switch to executable (not writable)
DWORD old;
VirtualProtect(mem, size, PAGE_EXECUTE_READ, &old);
// 4. Execute
((void(*)())mem)();
Common Offending Sections¶
| Section | Cause | Fix |
|---|---|---|
.textbss |
Incremental linking | /INCREMENTAL:NO |
.text |
Bad linker script | Check section attributes |
| Custom | Manual settings | Review /SECTION options |
Related Rules¶
- AD2010: DoNotMarkImportsSectionAsExecutable
- AD2016: MarkImageAsNXCompatible
- AD2019: DoNotMarkWritableSectionsAsShared