Skip to content

AD3010: EnableReadOnlyRelocations

Summary

Property Value
ID AD3010
Name EnableReadOnlyRelocations
Category Security
Severity Error
Applies to ELF (Linux/Unix)

Description

RELRO (Relocation Read-Only) ensures that the Global Offset Table (GOT) and other relocation sections are marked read-only after relocations are applied. This prevents attackers from overwriting function pointers in the GOT.

How It Works

The rule checks for the PT_GNU_RELRO segment in the ELF program headers:

  1. Presence of PT_GNU_RELRO segment
  2. Segment covers GOT and other relocation data
  3. Combined with BIND_NOW for full RELRO

Why This Matters

The Global Offset Table (GOT) is a prime target for attackers. By overwriting GOT entries, they can redirect any function call to attacker-controlled code. RELRO makes this attack significantly harder or impossible.

The GOT Attack

The GOT contains pointers to dynamically linked functions:

Normal execution:
  call printf@plt → GOT[printf] → libc printf()

After GOT overwrite:
  call printf@plt → GOT[printf] → attacker_function()

// Attacker only needs one write primitive to hijack any function

Why Attackers Love the GOT

Property Attack Advantage
Fixed location (no PIE) Easy to find
Contains code pointers Direct control flow hijack
Writable (without RELRO) Easy to modify
Many entries Many targets

Partial vs Full RELRO

No RELRO:
  .got           - Writable
  .got.plt       - Writable
  Result: All GOT entries attackable

Partial RELRO (-Wl,-z,relro):
  .got           - Read-only after load
  .got.plt       - WRITABLE (for lazy binding)
  Result: .got.plt still attackable

Full RELRO (-Wl,-z,relro,-z,now):
  .got           - Read-only after load
  .got.plt       - Read-only (immediate binding)
  Result: Entire GOT protected

Attack Surface Comparison

Configuration GOT Attack Possible
No RELRO Yes, any entry
Partial RELRO Yes, .got.plt entries
Full RELRO No (without memory corruption first)

Real-World Exploitation

GOT overwrites have been used in:

Attack Type Technique
Format string %n to write to GOT
Heap overflow Corrupt chunk, overwrite GOT
Use-after-free Fake object writes to GOT
Type confusion Wrong type, write to GOT

Performance Considerations

RELRO Type Startup Cost Runtime Cost
None Zero Zero
Partial Minimal Zero
Full Small (resolve all symbols) Zero

Full RELRO adds startup time proportional to the number of imported symbols. For most applications, this is negligible.

How RELRO Works

Load sequence with Full RELRO:
1. Load binary into memory
2. Resolve ALL dynamic symbols immediately
3. Fill entire GOT with resolved addresses
4. mprotect() GOT region to read-only
5. Begin execution

Now GOT cannot be modified!
  • GOT protection: Prevents overwriting function pointers
  • PLT attacks: Mitigates PLT/GOT overwrite exploits
  • Format string attacks: Harder to exploit
  • Defense in depth: Protects critical data structures

Types of RELRO

Partial RELRO

  • GOT is relocated after program header
  • Some sections made read-only
  • .got.plt remains writable
  • Enabled with -Wl,-z,relro

Full RELRO

  • All relocations done at load time
  • Entire GOT marked read-only
  • Requires -Wl,-z,relro -Wl,-z,now
  • Small startup overhead

Resolution

Partial RELRO (Minimum)

gcc -Wl,-z,relro source.c -o binary
gcc -Wl,-z,relro -Wl,-z,now source.c -o binary

CMake

# Full RELRO
add_link_options(-Wl,-z,relro -Wl,-z,now)

Makefile

LDFLAGS += -Wl,-z,relro -Wl,-z,now

When to Suppress

This rule may be suppressed for:

  • Plugin systems: Dynamic loading of many shared libraries
  • Startup-critical: Where lazy binding overhead matters
  • Legacy compatibility: Very old systems

Caveats

  • Full RELRO increases startup time (all symbols resolved immediately)
  • Partial RELRO leaves .got.plt writable
  • Some dynamic loading patterns may not work with full RELRO

Memory Layout

Without RELRO:
┌─────────────────┐
│   .got          │ Writable - attack target
├─────────────────┤
│   .got.plt      │ Writable - attack target
└─────────────────┘

With Partial RELRO:
┌─────────────────┐
│   .got          │ Read-only after relocation
├─────────────────┤
│   .got.plt      │ Still writable
└─────────────────┘

With Full RELRO:
┌─────────────────┐
│   .got          │ Read-only after relocation
├─────────────────┤
│   .got.plt      │ Read-only after relocation
└─────────────────┘

GOT Overwrite Attack

// Vulnerable pattern - GOT overwrite
// Attacker overwrites printf@got with system()
printf(user_input);  // Calls system() instead

// With full RELRO, GOT is read-only
// Write to GOT triggers SIGSEGV

References