2018-10-23 14:58:1424542人阅读
【Backer Talk】BSRC白客说第二期
概述
通常情况下,文件指针的利用有这几种方式:
覆盖vtable
覆盖fp
_IO_acquire_lock等溢出
FSOP
本议题将以一道比较简单的ctf题为例, _IO_acquire_lock中溢出的方式来缕清执行流程、熟悉结构体,进而分析FSOP的利用思路。
seethefile
> 题目来源:pwnable.tw
1.对照运行结果查看相应代码
文件打开读取关闭功能,打开的文件名不能带flag
发现写入全局变量name是可溢出覆盖到相邻的fp指针
2.触发崩溃
fclose+23 处提示有段错误,此时esi = aaaa,即我们覆盖的值
!!所以,我们覆盖的 fd 应为一个地址而不是一个字符串
3.分析原因
下载 glibc 2.23 源码搜索 _IO_new_fclose 可在 iofclose.c中看到定义:
同时发现覆盖的aaaa为结构体 IO_FILE,查看定义:
注意图中的chain
当 fp指针为正常指针时
来看看它的结构:
就是下边这么个链表:
每一个节点的开头为 _flags ,正常的 _flags 前两个字节为 0xfbad
exploit
直接结合程序来一步步绕过对结构体的检测
把溢出到fp 的内容换成地址 0x0804B260 即全局变量量 name 的地址
崩溃信息如下:
可以看到箭头指向的前 面 用 DWORD PTR [esi+0x48] 给 edx 为 0 ,导致箭头处访问0x8 处的内存导致出错。
不妨把 edx 设置为 一个地址:
仍存在段错误:
溢出的地方为edx寄存器,分析发现对应结构体中: _lock 成员的值
查看崩溃之前执行的指令
修正payload:
查看崩溃前指令
OK,已经很接近了,即:
eax = ebx + eax* 1 + 0x94 = bss_name + 0 + 0x94
那么我们可以通过设置 bss_name + 0x94 处的值从 而设置 eax ,进 而控制 call 的参数
如下步骤可以控制eip:
1.找出要调 用的函数地址记为func_addr,写 入某个可控区域记为func_ptr
2.将func_ptr - 0x44写入bss_name + 0x94
修正payload,下 面的payload将eip设置为0xcafebabe
另一种方法
上面介绍结构体的时候有个_chain ,这个是做什么的呢?
还有_IO_list_all 这个东东,看起来里边的东西都可以伪造,能不能利用呢?
在glibc源码中搜索_IO_list_all可以在genops.c中找到主要的几个使用了该结构体的函数:
void _IO_link_in (struct_IO_FILE_plus*fp)
void _IO_un_link (struct_IO_FILE_plus*fp)
int _IO_flush_all_lockp (intdo_lock)
重点来看第三个函数。
精简后的代码如下:
如果巧妙地控制判断的条件,伪造 _IO_list_all 结构体,则可以设置 fp 为任意地址。
全局搜索可以看到_IO_flush_all_lockp 在 abort.c 中是 fflush预处理后真实的样子,被函数 abort()调用。
往上继续跟踪,最后被assert()调用。
调用链为:
assert() __assert_fail()__assert_fail_base()abort() _IO_flush_all_lockp()
此时我们发现_IO_flush_all_lockp 出现的场景非常多,包括
glibc abort
exit()
main return
…
所有包含断言的地方都有。
由此,我们又多了个利用思路:
Ⅰ.伪造_IO_list_all
Ⅱ.通过某种 方式触发assert()从而调用_IO_flush_all_lockp使得fp为我们预期的地址
Ⅲ.利利 用_IO_flush_all_lockp对条件的判断时的一个预处理函数_IO_OVERFLOW达到利用效果
ps:有点像windows 下伪造异常处理理句句柄并通过触发异常来实现利用的过程。
文件结构体创建时涉及堆的操作,由此可以通过触发堆块检测异常来达到利用效果。
(from:angelboy’s topicon HITB SG2018)
//相关定义
//libioP.h
#define JUMP_FIELD(TYPE, NAME) TYPE NAME
//JUMP_FIELD(_IO_overflow_t, __overflow)
//_IO_overflow_t __overflow()
(*(struct _IO_jump_t **)
((void*) &_IO_JUMPS_FILE_plus (fp) + (fp)->_vtable_offset))
//genops.c
int __overflow (_IO_FILE*f,int ch)
{
/* This is a single-byte stream. */
if (f->_mode==0)
_IO_fwide (f,-1);
return _IO_OVERFLOW (f, ch);
}
libc_hidden_def (__overflow)
参考资料
Pwning My Life:HITCON CTF Qual 2016 - House of Orange Write up