【二进制安全】House of Force

原理

glibc 在空闲堆块无法满足要求的情况下会对 top chunk 进行操作,从它哪里得到新的满足用户需求的 chunk 块,House of Force的主要目标就是 top chunk,我们如果可以控制 top chunk 的 size 的话就可以使得 top chunk 指向任何位置,实现任意写。

但是 glibc 在分配堆块的时候也存在检查,不过这个检查很好绕过,因为它并不是专门来检测违规操作的,可以通过源码了解一下。

我们可以看出,唯一需要绕过的检查就是 size 的大小检查,我们在做 house of force 的时候必须把 top chunk 的 size 设置为很大的值,不过对于有堆溢出或者其他漏洞的程序来说,这个并不难。

一个很有用的做法就是把 top chunk 的 size 设置为 -1,因为在 malloc 源码中,大部分都是 unsinged 类型的数据,那么-1就会被当做这个类型的最大值从而轻松实现绕过。

从上面代码我们就可以看出,如果我们绕过检查了,我们实际上是可以控制 top chunk 的,进而我们就实现了任意地址写。

不过我们需要注意的是用户输入的 size 和堆分配的 chunk 的大小其实是不一样的,用户输入的 size 会经过一个内部的 checked_request2size 运算:

所以用户输入的 size 要经过运算使得 ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK 得出的结果满足需求,这个结果才是相应的堆块的chunk_size.而且需要绕过REQUEST_OUT_OF_RANGE的检查,这个的意思就是你输入的负值必须比 -2*MINSIZE 小(记得分清负数谁大谁小奥),这个一般都不用担心,同时我们要特别注意的是地址的对齐(32位8字节对齐,64位16字节对齐),因为 free 一个堆块的时候会进行地址对齐的检查。

同时,因为对 top chunk 的 chunk header 的设置,我们会改写指向的目标地址附近的值,这一点可以看一下上面源码里的 set_head。

总结:

程序存在漏洞我们可以控制 top chunk 的 size 域,例如堆溢出,off-by-one 这类

我们要可以自由的控制malloc的大小,因为像got表或者malloc_hook,free_hook,main_ret这些其实都和top chunk相隔甚远。

分配次数不能受限制(其实这个还好,很多ctf题目的这个条件都可以满足)

练习

1

HITCON training lab 11

我们先用 checksec 大体观察一下

可以看到只是开启了 Canary 和 NX,下面分析一下程序。

这个是add_item函数,可以看出堆块存储的数据结构大致是这样的:

然后一个结构体数组存放在bss段就构成了这个程序的主要堆块存取数据结构。

show函数就是通过变量结构体数组来把所有的堆块内容打印出来。

remove函数在free后把指针置0,同时也把size给置0了。

change函数存在明显的溢出漏洞,因为change的时候输入的size可以由用户自由控制,这就存在了堆溢出漏洞。

同时这个程序也存在后门magic()

利用方法

首先做好准备工作,哈哈。

按照上面讲的house of force,你就可以知道,我们只需要修改top chunk的size,然后算出用户申请多少size才能使得top chunk偏移到第一次申请的chunk处,然后我们改写改chunk的内容为为magic函数的地址来实现利用就可以了。

EXP

看到 exp 有人可能会疑惑第二个 malloc 那里为啥多减去一个 0xf 这个其实不是必须的,看上面的源码我们可以知道在计算 chunk size 的时候加上了 MALLOC_ALIGN_MASK,这里其实就是减去这个值,不过不是必须的,因为后面的与操作在对齐 chunk size 的时候就帮我们减去之前加上的这个值了。

另外 MALLOC_ALIGN_MASK = 2 * SIZE_SZ - 1 , 所以在 64 位里面就是 0xf 了。

上面这个算是一个实验,下面来一个题目类型的。

2

2016_bctf_bcloud

就逆向分析而言这个题目并不复杂,下面我只是大致分析一下题目的关键部分。

首先用checksec查看一下大致情况

开启了NX和canary

从new_note()函数我们可以得知堆块存储的大致数据结构,两个bss段的数组分别对应着堆块的mem部分指针和用户输入的size,同时用issync数组来标记是否同步。

delete_note()函数同时对指针和size置0,并且释放堆块。

利用思路

首先我们需要泄露地址,这个地址泄露的漏洞不是很好发现哈。。

第一眼看上去没什么问题,不过在strcpy的时候如果我们写入了0x40个字符,我们在栈上的数据就会和下面的指针连在一起,由于strcpy依赖"\x00"进行截断,所以会把我们数据下紧邻的堆块指针也复制到堆块里面,这样在printf的时候就会造成地址泄露。

这样我们就成功泄露了heap的地址,另一个漏洞在于init_org_host()函数:

这个漏洞类似于上面泄露地址时候的,也是由于strcpy,同样,让我们看一下如果我们输入最大数据量时栈里面的情况:

可以看到aaaa所在的区域的"\x00"截断已经没有了,那么在执行strcpy的时候会在org_ptr处造成溢出,而org_ptr处的下面一个chunk就是top chunk,所以如果我们精心构造第一次和第二次的输入就可以改造top chunk了实现house of force了。

house of force

我们改写top chunk的size为-1,然后算出note_size数组和top chunk之间的偏移使得top chunk指向note_size的上方,然后把note_size和note_list申请出来就可以实现溢出,进而实现利用。

利用思路

改写note_size,和note_list(这个是最主要目标)来实现任意写,实现任意写之后getshell的方式有很多,这里我们改写free@got为puts_plt,其实就是puts函数的地址,这样我们在delete的时候就可以把note_list上面对应的指针所指向的内容读取出来,这里我们可以把atoi@got写到note_list上来实现地址泄露,泄露成功之后我们可以把system的实际地址写入到atoi@got里面,因为在读入的时候都会使用这个函数,同时这个函数的参数是一个指针,那么我们输入的"/bin/sh\x00"就会被当做system函数的参数,这样我们就可以实现getshell了。

EXP

相关参考:

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/house_of_force-zh/

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200213A06EOE00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券