House of Force
House of Force
原理
Glibc的很多版本中(2.23和2.27),并没有对Top Chunk的Size字段进行完整性检查.
利用
1.先通过溢出到top chunk的size字段,伪造top chunk的大小。
2.然后malloc申请堆块并写入数据,实现任意地址任意写
详述
tip:
1.详述示例参考hacking everyday
2.用抽象的思维看此示例,不用过度纠结比如malloc对齐等细节,着重看如何实现House of Heap
分析程序
1.程序运行前输出了puts地址和heap开始的地址
2.给出菜单
3.选择malloc,申请一个0x18大小的堆块
(实际上分配了0x20大小)
在pwndbg里,用vis命令查看现在的堆
可以看到由于溢出了7个字节的b和1个字节的\x0a,可以覆盖top chunk的size
4.选择第二个选项,会输出变量target的值
x/gx &target指令(或者dp &target等其他指令)查看变量target地址附近的值
可以看到target变量地址比堆的起始地址要小。
而malloc函数只能继续向高地址申请空间,没办法向target变量靠拢。
漏洞利用
前置知识
程序执行malloc函数,先从bins中寻找满足需求的堆块.如果没有,再从top chunk中切下一块内存返回给malloc
malloc源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 victim = av->top;//获取当前top chunk的地址
size = chunksize (victim);//计算top chunk的大小
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
//MINSIZE就是堆块的最小size,32位程序为0x10,64位程序为0x20
//如果top chunk的大小大于nb(程序执行malloc需要分配的内存大小)
//加上MINSIZE的大小,就从top chunk中来切一块内存
//之所以要加上MINSIZE是要保证切割后剩余的内存要是一个完整的堆块
{
remainder_size = size - nb;//remainder_size为切割后的剩余大小
remainder = chunk_at_offset (victim, nb);
//remainder为切割前top chunk+nb的值,也就是切割后top chunk的地址
av->top = remainder;//更新top chunk
//下面两个set_head给切割出去的堆块以及切割后的top chunk设置新的size
set_head (victim, nb | PREV_INUSE |
(av != &main_arena ? NON_MAIN_ARENA : 0));
set_head (remainder, remainder_size | PREV_INUSE);
check_malloced_chunk (av, victim, nb);//调试用的,这里没用
void *p = chunk2mem (victim);//返回用户指针
alloc_perturb (p, bytes);
return p;
}
1.首先申请0x18的空间,并输入b’a’*24 + b’\xff’*8,由此可以把top chunk的size字段覆盖为’ffffffffffffffff’
问:为什么要填充成0xffffffffffffffff?
答:
源码中有如下代码:
1
2 if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
(size是切割前top_chunk的大小,nb是用户申请的大小)其中
1
2 1.nb = request + chunk_header
2.由下面的讲解可知:request_size = target_addr - 2 * chunk_header-victim由于request_size一般为负数,所以nb也大概率为负数.
负数强制转化为无符号数–即求负数的补码–除符号位,原码取反+1.
所以比较式右边很大.
因此要想比较式成立,我们要将top chunk的size改为-1,如此一来,对size强制转化为无符号数之后,会变成0xffffffffffffffff,一定满足size>nb+MINSIZE问:那为什么不能直接覆盖为-1,而是要覆盖为0xffffffffffffffff?
答:
如下为chunk的源码
1
2
3
4
5
6
7
8 struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
};其中size的数据类型为无符号整型
2.然后,申请(0xffffffff-0x603000)+0x602010-0x20-0x20大小的chunk
计算分配大小的原理
源码中有如下代码:
1
2 remainder = chunk_at_offset (victim, nb)
//victim(切割前top_chunk的地址) + nb (要申请的内存大小) = top_chunk (切割后top_chunk的地址)如果我们改变nb的值,就可以让切割后top chunk的地址变为目标地址
其中:
1
2
3 1.nb = request_size + chunk_header
2.top_chunk + chunk_header = target_addr
(因为是通过往top_chunk的usr data区里输入数据,来达到修改target_addr的效果)整合得:
request_size = target_addr - 2 * chunk_header-victim (request_size为负数)
tip
1.House of Force可以在tcache存在的libc中使用
2.
