Fsop Exploit File Structure

2018-10-15

百度安全Backer Talk 第二期议题 #fsop

大纲

  • 概述
  • 结合题目 seethefile熟悉文件指针结构,分析文件流函数执行过程
  • exploit
  • 另一种文件流劫持方法
  • 参考资料

概述

通常情况下,文件指针的利用有这几种方式:

  • 覆盖vtable
  • 覆盖fp
  • _IO_acquire_lock等溢出
  • FSOP

本议题将以一道比较简单的ctf题为例, _IO_acquire_lock中溢出的方式来缕清执行流程、熟悉结构体,进而分析FSOP的利用思路。

seethefile

题目来源:pwnable.tw

  • 对照运行结果查看相应代码 fsop1
  • 文件打开读取关闭功能,打开的文件名不能带flag fsop2
  • 发现写入全局变量name是可溢出覆盖到相邻的fp指针

  • 触发崩溃 fsop3 fsop4fclose+23处提示有段错误,此时esi= aaaa,即我们覆盖的值

!!所以,我们覆盖的fd应为一个地址而不是一个字符串

  • 分析原因 下载glibc 2.23源码搜索_IO_new_fclose可在iofclose.c中看到定义: fsop5 同时发现覆盖的aaaa为结构体IO_FILE,查看定义: 注意图中的 chain fsop6
  • fp指针为正常指针时 fsop7
  • 来看看它的结构: fsop8 fsop9 就是下边这么个链表: fsop10 每一个节点的开头为_flags,正常的_flags前两个字节为0xfbad

exploit

  • 直接结合程序来一步步绕过对结构体的检测,把溢出到fp的内容换成地址0x0804B260即全局变量name的地址 fsop11 * 崩溃信息如下 fsop12 可以看到箭头指向的前面用 DWORD PTR [esi+0x48]edx0,导致箭头处访问0x8处的内存导致出错。
  • 不妨把edx设置为一个地址: fsop13
  • 仍存在段错误: 溢出的地方为edx寄存器,分析发现对应结构体中:_lock成员的值 fsop14 fsop15
  • 查看崩溃之前执行的指令 fsop15
  • 修正payload: fsop16 fsop17
  • 查看崩溃前指令 fsop18

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 fsop19 fsop20

    另一种方法

    上面介绍结构体的时候有个_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 (int do_lock)

重点来看第三个函数。

  • 精简后的代码如下: fsop21如果巧妙地控制判断的条件,伪造_IO_list_all结构体,则可以设置fp为任意地址。

全局搜索可以看到_IO_flush_all_lockpabort.c中是fflush预处理后真实的样子,被函数abort()调用。

  • 往上继续跟踪,最后被assert()调用。

  • 调用链为: assert() -> __assert_fail() -> __assert_fail_base() -> abort() -> _IO_flush_all_lockp()

此时我们发现_IO_flush_all_lockp出现的场景非常多,包括

  • glibc abort
  • exit()
  • main return
  • … 所有包含断言的地方。

由此,我们又多了个利用思路:

  1. 伪造_IO_list_all
  2. 通过某种方式触发assert()从而调用_IO_flush_all_lockp使得fp为我们预期的地址
  3. 利用_IO_flush_all_lockp对条件的判断时的一个预处理函数_IO_OVERFLOW达到利用效果 ps:有点像windows 下伪造异常处理句柄并通过触发异常来实现利用的过程。

文件结构体创建时涉及堆的操作,由此可以通过触发堆块检测异常来达到利用效果。

fsop22 (from:angelboy’s topic on 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)


  • 当fsop由堆结构校验出错触发时,该利用方法又叫house of orange

其他

  • 需要注意的是,glibc 2.24开始,添加了对_IO_file->vtable的检查,此时需要如果用该方法需要绕过该检查。
  • 从glibc2.26开始,malloc_printerr()移除了_IO_flush_all_lockp()函数,house of orange方法将失效,但是fsop仍可通过其他包含断言的函数错误触发。

参考资料

附件:FSOP-exploit-file-structure.pdf