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:
- Presence of
PT_GNU_RELROsegment - Segment covers GOT and other relocation data
- 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.pltremains 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)¶
Full RELRO (Recommended)¶
CMake¶
Makefile¶
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.pltwritable - 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