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¶
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
Pass: GOT is protected (Full RELRO)
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¶
- AD3010: EnableReadOnlyRelocations - Partial RELRO
- AD3011: EnableBindNow - Full RELRO
- GOT and PLT for pwning