杭电大狮虎的一道题,感觉还是挺绕的,漏洞为double free….
分析
编译方式为gcc mistake.c -z execstack -no-pie -o mistake
则无NX,再看源码:
1 | //# mistake.c |
如上,首先分配0x31个chunk,再删除第0x2f(从0编号)个chunk(命名为chunk0x2f,其他同理),由于id不大于chunk_number,值合法,则释放chunk0x2f,但是由于id不小于0x2f则不会进行循环里的操作,此时chunk_number值为0x2f、list[0x2f]值为已释放的chunk,再次删除chunk0x2f即造成double free,但是由于fastbins存在double free的检查,这里不能直接这样释放,可以按照free(chunk0x2f)->free(chunk0x00)->freechun(0x2e)的方式释放:
1 | if(id > chunk_number){ |
此时chunk_number值为0x2d,但是由于chunk个数有上限,此时最多在分配3个chunk,而要分配第4个chunk才能利用此漏洞,解决方法是,降低下限以增大范围,增加可容纳的chunk数,方法是free(chunk-x),此处的chunk-x值应该为0,否则会检查chunk并将其加入fastbins,而且他应该竟可能靠近list,以减少它对程序正常功能的影响。另一个地方,由于i可为负数下溢,导致free参数可控,可使用house of spirit,用它来让指向某个位置的指针作为参数以控制那个位置,当然要考虑过检查。最后,由于开了NX,首先的想法就是将shellcode写入数据区,然后修改关键指针,如返回地址、GOT项,又由于开了PIE所有位置都不固定,只能先泄露一个地址。至于泄露,chunk0x2f地址释放后没有被chunk0x30地址覆盖,而且还能输出,则可以泄露出堆地址。
利用
现在来理一下利用思路,最终方法是先将shellcode写入已知的地方,然后覆写GOT为shellcode地址:
- 首先分配0x31个chunk,list是从第一项开始使用的,即第一个chunk保存在list[0x00]里面,则约定第一个chunk为chunk0x00,那么chunk0x31被保存在list[0x30]中,此时chunk_number为0x31,这里分配的第二个chunk里面应填入
p64(0)+p64(0x21)
,其他填shellcode第二部分,会有用的。 - 释放chunk0x2f,则只会释放它,而不会进入for循环内部,也就不会将list[0x2f]的值改掉,它仍然指向被释放了的chunk0x2f,此时chunk_number为0x2f。
- 释放chunk0x00,则会进入for,使list[0x2e]=chunk0x2f,此时chunk_number为0x2e。
- 释放list[0x2e](即chunk0x2f),chunk0x2f被double free啦,但是目前还没办法去利用它,因为分配的chunk有个数限制,且释放掉的会加入fastbins,它的特点是FILO,此时chunk_number为0x2d。
- 观察下面的bss变量位置,chunk_number距离list为0x20字节,将chunk_number视为8字节,则它们刚好还有3个pointer的空间,且这片区域初始为0,那么释放3次list[-0x03],list[-0x03]=chunk0x01,此时chunk_number为0x2a,根据libc代码只要size在0x2*就能过检测。
1
2
3
4
5
6
7
8
9
10
11
12if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))
{
...
((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
errstr = "malloc(): memory corruption (fast)";
....
return p;
}
} - 分配chunk0x2f,被放在list[0x2b],写入数据
p64(&chunk_number-0x08)
,因为chunk0x2f还在fastbin中,所以覆盖了fd指针,指向了一块已知的地方。 - 分配chunk0x01,被放在list[0x2c]。
- 分配chunk0x2f,被放在list[0x2d],此时fastbin对应项指向了
&chunk_number-0x08
。 - 再次分配即可得到它,向其中写入
p64(-17)
,被放在list[0x2e],此时fastbin对应项指向了list[-0x03]=chunk0x02
。 - 释放list[-0x05],则chunk_number值被修改位-17啦,再次分配,则list[-17+1]=chunk0x02,即将writeGot改为shellcode地址,在其中写入shellcode。这里面有个问题,就是shellcode长度是大于0x10的,不能直接写完,可以分成几个部分,使用相对跳转即可,例如chunk0x02与chunk0x03是相邻的,即可在它们里面写入完整shellcode。
最终代码如下:
1 | #!/usr/bin/env python |
结果
由于当前系统libc版本是2.26存在tcache,自己编的有nx,所以本机挂了,先睡觉,有时间再弄,如下成功劫持PC到shellcode处: