ZCTF note3:一种新解法

最近在学习unlink做到了这道题,网上有两种做法:一种是利用edit功能读入id时整数溢出使索引为-1,一种是设置块大小为0使得写入时利用整数溢出漏洞可以将数据溢出到下一个块中 。我采取了另一种思路:程序在分配id=7块时虽然提示块已满 , 但没有采取措施,依然分配了一个块,并将块地址放在了存放块0 size的位置,使得可以往块0写入足够多的数据溢出到下一个块中 。
我先分析我的解法,然后再简单叙述一下另外两种解法的原理 。
程序分析一般步骤查看程序保护措施 。

ZCTF note3:一种新解法

文章插图
该程序有4个功能:
  • New note
  • Show note(假的,只打印一个字符串)
  • Edit note
  • Delete note
New功能添加note函数如下图,主要流程已通过注释标注 。值得注意的是当i=7时,虽然提示note已满,添加失败,但没有return语句,后面依然为它分配块并将地址保存在&ptr+7处 。(注意:i=0时块的size保存在qword_6020C0[0+8]处)
ZCTF note3:一种新解法

文章插图
需要关注的是qword_6020C0和ptr的关系,其内存关系如下所示
.bss:00000000006020C0 ; __int64 qword_6020C0[].bss:00000000006020C0 qword_6020C0    dq ?                    ; DATA XREF: sub_400A30+D1↑w.bss:00000000006020C0                                         ; sub_400A30+E6↑w ....bss:00000000006020C8 ; void *ptr.bss:00000000006020C8 ptr             dq ?                    ; DATA XREF: sub_400A30+16↑r.bss:00000000006020C8                                         ; sub_400A30+BC↑w ....bss:00000000006020D0                 dq ?.bss:00000000006020D8                 dq ?.bss:00000000006020E0                 dq ?.bss:00000000006020E8                 dq ?.bss:00000000006020F0                 dq ?.bss:00000000006020F8                 dq ?.bss:0000000000602100                 dq ?.bss:0000000000602108                 dq ?可以看到ptr所在位置等同于qword_6020C0[1]所在位置,所以当i=7时分配的块地址保存在&ptr+7等同于保存在qword_6020C0[8]处,即表示i=0块的大小 。通过分配i=7块可实现i=0块大小被新分配块地址覆写,而块地址所代表的大小足够我们溢出到后面的块内 。
Show功能该功能没什么用,只打印一串字符串 。
ZCTF note3:一种新解法

文章插图
Edit功能如图,主要操作通过注释的方式介绍 。
ZCTF note3:一种新解法

文章插图
Delete功能
ZCTF note3:一种新解法

文章插图
qword_6020C0[0]可以理解为最近操作过的块地址 。
漏洞利用漏洞利用思路如下:
1.unlink
添加7个块后 , 再添加一个块(i=7),这时块0的大小会被改的很大(值为块7的地址) , 然后在块0中构造fake_chunk并溢出到下一个块修改header数据实现unlink 。需要注意第i=1个块时大小要超过fastbin的范围 。
2.泄露地址
unlink后可以实现任意写 。为了泄露函数地址 , 需要执行输出函数,可以将free@got值改为puts@plt值,然后将块i的地址改为puts@got的地址,这时调用删除功能free(块i)就可以输出puts@got的值 , 从而得到动态链接库加载地址,进一步得到system地址 。

推荐阅读