Skip to content

AD3022: WritableGotProtection

Summary

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

Description

This rule checks that the Global Offset Table (GOT) is protected and not writable after program initialization. A writable GOT is a prime target for attackers to redirect function calls to malicious code.

Why This Matters

The Global Offset Table is one of the most valuable targets for attackers. A single write to the GOT can redirect any dynamically-linked function call to attacker-controlled code, providing immediate code execution.

Why Attackers Target the GOT

GOT contains:
  printf  → 0x7fff12345678  (libc printf)
  malloc  → 0x7fff23456789  (libc malloc)
  exit    → 0x7fff34567890  (libc exit)

After GOT overwrite:
  printf  → 0x7fff12345678  (unchanged)
  malloc  → 0x41414141      (attacker controlled!)
  exit    → 0x7fff34567890  (unchanged)

Next malloc() call → attacker's code

Attack Simplicity

Attack Step GOT Attack Alternative
1. Get write primitive Same Same
2. Choose target Write to GOT[func] Find and corrupt vtable/pointer
3. Wait for trigger Program calls func Hope code path hit
4. Result Immediate code exec Maybe works

GOT attacks are reliable and predictable.

Common Attack Vectors

Vulnerability GOT Exploitation
Format string %n to write GOT entry
Heap overflow Corrupt chunk to overwrite GOT
Use-after-free Fake object writes to GOT
Arbitrary write Direct GOT modification

Full RELRO Protection

Without RELRO:
  GOT at fixed offset from binary base
  GOT is RW (Read-Write) forever
  Every write primitive = GOT attack potential

With Partial RELRO:
  .got protected after load
  .got.plt still writable (for lazy binding)
  Attack surface reduced but not eliminated

With Full RELRO:
  All symbols resolved at load time
  Entire GOT marked read-only (mprotect)
  GOT attacks require additional memory corruption

Defense Effectiveness

Configuration GOT Overwrite Possible
No RELRO Yes, trivially
Partial RELRO Yes, .got.plt entries
Full RELRO No (without mprotect bypass)

Performance Trade-off

Aspect Lazy Binding Immediate Binding (Full RELRO)
Startup time Fast Slightly slower
First function call Slow (resolve) Normal
Subsequent calls Normal Normal
Security Weak Strong

For most applications, the startup overhead is negligible.

  • GOT overwrite attacks: Attackers can redirect function pointers
  • Control flow hijacking: Overwriting GOT entries allows arbitrary code execution
  • PLT attacks: The Procedure Linkage Table uses GOT for dynamic resolution
  • Defense in depth: GOT protection complements ASLR and other mitigations

How the GOT Works

The GOT stores addresses of dynamically linked functions: 1. First call goes through PLT → dynamic linker resolves → writes to GOT 2. Subsequent calls read directly from GOT 3. If GOT is writable, attackers can overwrite these addresses

How to Fix

Enable Full RELRO

# Full RELRO makes GOT read-only after initialization
gcc -Wl,-z,relro -Wl,-z,now -o myapp myapp.c

Relationship with RELRO

Protection Level GOT Status Flags
No RELRO Fully writable (none)
Partial RELRO Mostly writable -Wl,-z,relro
Full RELRO Read-only after init -Wl,-z,relro -Wl,-z,now

Example

Fail: GOT is writable

.got section is WRITE
BIND_NOW not set

Pass: GOT is protected (Full RELRO)

GNU_RELRO segment present
BIND_NOW flag set
.got section is READ-ONLY after initialization

Performance Consideration

Full RELRO with BIND_NOW causes all symbols to be resolved at load time, which increases startup time. For most applications, this overhead is negligible.

See Also