Skip to content

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:

  1. First build: Linker leaves gaps between functions
  2. Subsequent builds: Modified functions fit in gaps
  3. Benefit: Faster linking during development
  4. 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:

# Bad - creates .textbss (W+X)
link.exe /INCREMENTAL ...

# Good
link.exe /INCREMENTAL:NO ...

Project Properties

  1. Open Project Properties
  2. Navigate to Linker → General
  3. 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

  • .textbss is 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

References