Pwn with File结构体(一)
前言
本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274
利用 FILE 结构体进行攻击,在现在的 ctf 比赛中也经常出现,最近的 hitcon2017 又提出了一种新的方式。本文对该攻击进行总结。
正文
首先来一张 _IO_FILE 结构体的结构

_IO_FILE_plus 等价于 _IO_FILE + vtable
调试着来看看(64 位)

vtable 指向的位置是一组函数指针
利用 vtable 进行攻击
通过一个 uaf 的示例代码来演示
|
|
- 首先分配一个
_IO_FILE_plus大小的内存块 - 然后释放掉调用
fopen分配_IO_FILE_plus结构体 - 修改
fp的vtable指针到我们布局的地址 - 调用
fclose函数, 进而调用pwn

调试可以看到,分配的大小为 0xf0(也就是 0xe0+0x10) 和_IO_FILE_plus 的大小是一样的

free 掉后,调用 fopen 会占用这个内存
查看 vtable 也是符合预期
替换vtable指针之后
close 函数已经被修改为 pwn 函数,最后调用 fclose 函数,就会调用 pwn 函数
house of orange
为了便于调试,使用 how2heap 的代码进行调试分析。
|
|
代码的流程如下:
- 首先分配
0x400字节的块 - 修改
top chunk的size域为0xc01 malloc(0x1000)触发_int_free,top被放到了unsorted bin, 下面称它为old_top- 布局
old_top, 设置bk = io_list_all - 0x10, 把old_top伪造成一个_IO_FILE_plus,并设置好vtable malloc(10)由于此时fastbin,smallbin均为空,所以会进入遍历unsorted bin,并根据相应的大小放到对应的bin中。上一步设置old_top大小为0x60, 所以在放置old_top过程中,先通过unsorted bin attack修改io_list_all为fd也就是 main_arena->top, 然后old_top会被链到smallbin[5](大小为 0x60 ), 接着继续遍历unsorted bin,这一步 会abort,原理下面说, 然后会遍历io_list_all调用_IO_OVERFLOW (fp, EOF). 伪造vtablegetshell。
下面调试分析之
参考断点:
|
|
调试到
|
|
top chunk 的 size 已经被修改,unsorted bin 还是空的。

单步步过,发现 top 已经被 添加到 unsorted bin

然后就是一系列的伪造 _IO_FILE_plus 操作, 直接运行到
看看布局好后的结果

vtable
可以看到 __overflow 被设置为 winner 函数,所以只要调用 __overflow 就会调用 winner 。
下面看看,怎么通过堆布局实现 getshell
在 malloc.c:3472 下好断点,运行,会被断下来。
这里是遍历 unsorted bin 的流程。

会进入这里原因在于此时 fastbin , smallbin 均为空,不能满足分配的需求,接着就会进入这里。
这里会有一个 check ,过不去就会 malloc_printerr ,进而 abort 。
第一次进入这里是可以过去的,然后会根据大小把 victim 放到合适的 bin 中,之前我们已经 把 old_top 的大小设置成了 0x60, 这里他就会被放到 smallbin[5] 里。
同时插入之前会先从unsorted bin 中 unlink (unsorted bin attack) ,这时可以 往 victim->bk + 0x10 写入 victim->fd, 之前我们已经设置 victim->bk 为 _IO_list_all-0x10, 所以在这里就可以 修改 _IO_list_all 为 main_arena->top
第一次遍历 unsorted bin, 从 unsorted bin 移除时的相关变量,内存数据。

可以看到 bck 会成为unsorted bin 的起始位置,然后
而且此时 bck->fd 为 _IO_list_all。
继续运行,再次断在了 malloc.c:3472。

可以看到,此时的 _IO_list_all 已经被修改成了 <main_arena+88>, old_top 被放到了 smallbin[5], 而且此时 victim->size 为0, 所以下面会进入 abort 的流程。
我们来看看,此时构造的 _IO_list_all 的内容

_IO_list_all 偏移 0x68 为 _chain ,这也是之前设置 old_top 大小为 0x60 的原因。
这样就成功把 old_top 链入了 _IO_list_all。
下面看看该怎么拿 shell
在 abort 函数中会调用 fflush(null)

实际调用的是 _IO_flush_all_lockp

遍历 _IO_list_all 调用 _IO_OVERFLOW (fp, EOF),其实就是调用 fp->vtable->__overflow(fp,eof)
第一次执行循环时,可以看上面的 _IO_list_all 数据,发现进入不了 _IO_OVERFLOW 这个判断,所以_IO_list_all 第一项的 vtable 中的数据是坏的也没有关系。

第二次循环,通过 fp = fp->_chain 找到我们的 old_top, 我们已经在这布局好了数据。

运行 getshell
总结
FILE 结构体是一个很好的攻击目标,学习一下很有必要
调试时,尽可能用最小的代码复现问题。
参考链接:
http://www.evil0x.com/posts/13764.html
https://securimag.org/wp/news/buffer-overflow-exploitation/
https://outflux.net/blog/archives/2011/12/22/abusing-the-file-structure/
http://repo.thehackademy.net/depot_ouah/fsp-overflows.txt
本站文章均原创, 转载注明来源
本文链接:http://blog.hac425.top/2017/12/07/pwn_with_file_part1.html