IO FILE 之挟制vtable及FSOP | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

IO FILE 之挟制vtable及FSOP

申博_安全预警 申博 42次浏览 未收录 0个评论

之前的文章对IO FILE相干功用函数的源码举行了剖析,后续将对IO FILE相干的应用举行论述。

传送门:

  • IO FILE之fopen详解
  • IO FILE之fread详解
  • IO FILE之fwrite详解
  • IO_FILE之fclose详解

经过了前面关于fopen等源码的引见,晓得了IO FILE组织体内里有个很重要的数据组织–vtable,IO函数的很多功用都是经由历程它去完成的。接下来重要形貌怎样经由历程挟制vtable去完成掌握函数实行流以及经由历程FSOP来举行应用。

vtable挟制

本文是基于libc 2.23及之前的libc上可实行的,libc2.24以后加入了vtable check机制,没法再组织vtable。

vtable是_IO_FILE_plus组织体里的一个字段,是一个函数表指针,内里存储着很多和IO相干的函数。

挟制道理

_IO_FILE_plus组织体的定义为:

struct _IO_FILE_plus
{
  _IO_FILE file;
  const struct _IO_jump_t *vtable;
};

vtable对应的组织体_IO_jump_t的定义为:

struct _IO_jump_t
{
    JUMP_FIELD(size_t, __dummy);
    JUMP_FIELD(size_t, __dummy2);
    JUMP_FIELD(_IO_finish_t, __finish);
    JUMP_FIELD(_IO_overflow_t, __overflow);
    JUMP_FIELD(_IO_underflow_t, __underflow);
    JUMP_FIELD(_IO_underflow_t, __uflow);
    JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
    /* showmany */
    JUMP_FIELD(_IO_xsputn_t, __xsputn);
    JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
    JUMP_FIELD(_IO_seekoff_t, __seekoff);
    JUMP_FIELD(_IO_seekpos_t, __seekpos);
    JUMP_FIELD(_IO_setbuf_t, __setbuf);
    JUMP_FIELD(_IO_sync_t, __sync);
    JUMP_FIELD(_IO_doallocate_t, __doallocate);
    JUMP_FIELD(_IO_read_t, __read);
    JUMP_FIELD(_IO_write_t, __write);
    JUMP_FIELD(_IO_seek_t, __seek);
    JUMP_FIELD(_IO_close_t, __close);
    JUMP_FIELD(_IO_stat_t, __stat);
    JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
    JUMP_FIELD(_IO_imbue_t, __imbue);
#if 0
    get_column;
    set_column;
#endif
};

这个函数表中有19个函数,离别完成IO相干的功用,由IO函数挪用,如fwrite终究会挪用__write函数、fread会挪用__doallocate来分派IO缓冲区等。

给出stdin的IO FILE组织体和它的虚表的值,更直观的看下,首先是stdin的组织体:
IO FILE 之挟制vtable及FSOP
可以看到此时的函数表的值是 0x7fe23cc576e0 <__GI__IO_file_jumps>,检察它的函数:
IO FILE 之挟制vtable及FSOP

vtable挟制的道理是:假如可以掌握FILE组织体,完成对vtable指针的修正,使得vtable指向可控的内存,在该内存中组织好vtable,再经由历程挪用响应IO函数,触发vtable函数的挪用,即可挟制顺序实行流。

从道理中可以看到,挟制最症结的点在于修正IO FILE组织体的vtable指针,指向可控内存。平常来讲有两种体式格局:一种是只修正内存中已有FILE组织体的vtable字段;另一种则是捏造全部FILE组织体。固然,两种的实质终究都是修正了vtable字段。

demo示例顺序可以参考ctf wiki中的示例:

#define system_ptr 0x7ffff7a52390;

int main(void)
{
    FILE *fp;
    long long *vtable_addr,*fake_vtable;

    fp=fopen("123.txt","rw");
    fake_vtable=malloc(0x40);

    vtable_addr=(long long *)((long long)fp+0xd8);     //vtable offset

    vtable_addr[0]=(long long)fake_vtable;

    memcpy(fp,"sh",3);

    fake_vtable[7]=system_ptr; //xsputn

    fwrite("hi",2,1,fp);
}

这个示例经由历程修正已有FILE组织体的内存的vtable,使其指向用户可控内存,完成挟制顺序实行system("sh")的历程。

有了前面几篇文章对vtable挪用的基本,挟制的道理明白就比较轻易了,不再赘述。

IO挪用的vtable函数

在这里给出fopenfreadfwritefclose四个函数会挪用的vtable函数,之前在每篇文章的末端都已给出,在这里一致总结下,轻易背面应用的时刻可以较快的找到所需挟制的函数指针。

fopen函数是在分派空间,竖立FILE组织体,未挪用vtable中的函数。

fread函数中挪用的vtable函数有:

  • _IO_sgetn函数挪用了vtable的_IO_file_xsgetn
  • _IO_doallocbuf函数挪用了vtable的_IO_file_doallocate以初始化输入缓冲区。
  • vtable中的_IO_file_doallocate挪用了vtable中的__GI__IO_file_stat以猎取文件信息。
  • __underflow函数挪用了vtable中的_IO_new_file_underflow完成文件数据读取。
  • vtable中的_IO_new_file_underflow挪用了vtable__GI__IO_file_read终究去实行体系挪用read。

fwrite 函数挪用的vtable函数有:

  • _IO_fwrite函数挪用了vtable的_IO_new_file_xsputn
  • _IO_new_file_xsputn函数挪用了vtable中的_IO_new_file_overflow完成缓冲区的竖立以及革新缓冲区。
  • vtable中的_IO_new_file_overflow函数挪用了vtable的_IO_file_doallocate以初始化输入缓冲区。
  • vtable中的_IO_file_doallocate挪用了vtable中的__GI__IO_file_stat以猎取文件信息。
  • new_do_write中的_IO_SYSWRITE挪用了vtable_IO_new_file_write终究去实行体系挪用write。

fclose函数挪用的vtable函数有:

  • 在清空缓冲区的_IO_do_write函数中会挪用vtable中的函数。
  • 封闭文件形貌符_IO_SYSCLOSE函数为vtable中的__close函数。
  • _IO_FINISH函数为vtable中的__finish函数。

其他的IO函数功用相类似的挪用的应当都差不多,可以参考下。

FSOP

FSOP全称是File Stream Oriented Programming,症结点在于前面fopen函数中形貌过的_IO_list_all指针。

历程中翻开的一切文件组织体运用一个单链表来举行治理,即经由历程_IO_list_all举行治理,在fopen的剖析中,我们晓得了fopen是经由历程_IO_link_in函数将新翻开的组织体链接进入_IO_list_all的,相干的代码以下:

fp->file._flags |= _IO_LINKED;
...
fp->file._chain = (_IO_FILE *) _IO_list_all;
_IO_list_all = fp;

从代码中也可以看出来链表是经由历程FILE组织体的_chain字段来举行链接的。

平常的举行中存在stderr、sdout以及stdin三个IO FILE,此时_IO_list_all以下:
IO FILE 之挟制vtable及FSOP
IO FILE 之挟制vtable及FSOP
IO FILE 之挟制vtable及FSOP
IO FILE 之挟制vtable及FSOP
构成的链表以下图所示:
IO FILE 之挟制vtable及FSOP

看到链表的操纵,应当就大抵猜到了FSOP的重要道理了。即经由历程捏造_IO_list_all中的节点来完成对FILE链表的掌握以完成应用目标。平常来讲平常是直接应用恣意写的破绽修正_IO_list_all直接指向可控的地点。

详细来讲该怎样应用呢?glibc中有一个函数_IO_flush_all_lockp,该函数的功用是革新一切FILE组织体的输出缓冲区,相干源码以下,文件在libio\genops中:

int
_IO_flush_all_lockp (int do_lock)
{
  int result = 0;
  struct _IO_FILE *fp;
  int last_stamp;

  fp = (_IO_FILE *) _IO_list_all;
  while (fp != NULL)
    {
        ...
      if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
       || (_IO_vtable_offset (fp) == 0
           && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
                    > fp->_wide_data->_IO_write_base))
#endif
       )
      && _IO_OVERFLOW (fp, EOF) == EOF)   //,假如输出缓冲区有数据,革新输出缓冲区
    result = EOF;


    fp = fp->_chain; //遍历链表
    }
...
}

经由历程对fwrite剖析,我们晓得输出缓冲区的数据保留在fp->_IO_write_base处,且长度为fp->_IO_write_ptr-fp->_IO_write_base,因而上面的if语句实质上是推断该FILE组织输出缓冲区是不是另有数据,假如有的话则挪用_IO_OVERFLOW去革新缓冲区。个中_IO_OVERFLOW是vtable中的函数,因而假如我们可以掌握_IO_list_all链表中的一个节点的话,就有能够掌握顺序实行流。

剖析某旺ActiveX控件ImageMan.dll栈溢出破绽

0x1:漏洞介绍 阿里旺旺ActiveX控件ImageMan.dll动态链接库中有一个函数AutoPic,由于未对传入的参数长度进行校验导致栈溢出,在拷贝缓冲区数据时会读取到不可用地址,从而使程序进入SEH异常处理流程,通过计算好缓冲区到SEH异常处理结构的位置用指定长度的字符串精确覆盖SEH指针从而控制程序执行流程达到执行任意代码的效果。 0x2:漏洞分析环境 操作系统:windowsXP SP3 阿里旺旺版本:AliIM2010_taobao(6.50.00C) 调试器:OD 0x3:漏洞复现 POC代码如下:




搭建好环境

可以看出来该函数的意义是为了保证数据不丧失,因而在顺序实行退出相干代码时,会去挪用函数去革新缓冲区,确保数据被保留。依据_IO_flush_all_lockp的功用,猜想这个函数应当是在顺序退出的时刻举行挪用的,因为它革新一切FILE的缓冲区。事实上,会_IO_flush_all_lockp挪用函数的机遇包含:

  • libc实行abort函数时。
  • 顺序实行exit函数时。
  • 顺序从main函数返回时。

再多做一点操纵,去看下上述三种状况的客栈,来进一步相识顺序的流程。将断点下在_IO_flush_all_lockp,检察栈组织。

首先是abort函数的流程,应用的double free破绽触发,栈回溯为:

_IO_flush_all_lockp (do_lock=do_lock@entry=0x0)
__GI_abort ()
__libc_message (do_abort=do_abort@entry=0x2, fmt=fmt@entry=0x7ffff7ba0d58 "*** Error in `%s': %s: 0x%s ***\n")
malloc_printerr (action=0x3, str=0x7ffff7ba0e90 "double free or corruption (top)", ptr=<optimized out>, ar_ptr=<optimized out>)
_int_free (av=0x7ffff7dd4b20 <main_arena>, p=<optimized out>,have_lock=0x0)
main ()
__libc_start_main (main=0x400566 <main>, argc=0x1, argv=0x7fffffffe578, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe568)
_start ()

exit函数,栈回溯为:

_IO_flush_all_lockp (do_lock=do_lock@entry=0x0)
_IO_cleanup ()
__run_exit_handlers (status=0x0, listp=<optimized out>, run_list_atexit=run_list_atexit@entry=0x1)
__GI_exit (status=<optimized out>)
main ()
__libc_start_main (main=0x400566 <main>, argc=0x1, argv=0x7fffffffe578, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe568)
_start ()

顺序平常退出,栈回溯为:

_IO_flush_all_lockp (do_lock=do_lock@entry=0x0)
_IO_cleanup ()
__run_exit_handlers (status=0x0, listp=<optimized out>, run_list_atexit=run_list_atexit@entry=0x1)
__GI_exit (status=<optimized out>)
__libc_start_main (main=0x400526 <main>, argc=0x1, argv=0x7fffffffe578, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe568)
_start ()

看出来顺序平常从main函数返回后,也是挪用了exit函数,所以终究才挪用_IO_flush_all_lockp函数的。

再说怎样应用,应用的体式格局为:捏造IO FILE组织体,并应用破绽将_IO_list_all指向捏造的组织体,或是将该链表中的一个节点(_chain字段)指向捏造的数据,终究触发_IO_flush_all_lockp,绕过搜检,挪用_IO_OVERFLOW时完成实行流挟制。

个中绕过搜检的前提是输出缓冲区中存在数据:

if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
       || (_IO_vtable_offset (fp) == 0
           && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
                    > fp->_wide_data->_IO_write_base))

示例--house of orange

FSOP的应用示例,最典范的莫过于house of orange进击要领。下面将经由历程house of orange进击要领详细表现vtable挟制和fsop,示例题是东华杯2016-pwn450的note。

先申明一下,顺序中运用的unsorted bin attack改写_IO_list_all,运用sysmalloc获得unsorted bin的道理我将不再详细形貌,有须要的可以参考unsorted bin attack剖析,这里重要集合在vtable的挟制以及FSOP的完成上。

问题是一道菜单题,可以建立、编辑、以及删除堆块,个中只允许同时对一个堆块举行操纵,只要开释了当前堆块才可以请求下一个堆块。

在建立函数中,堆块被malloc出来后会打印堆的地点,可以运用该函数来泄漏堆地点;破绽在编辑函数中,编辑函数可以输入恣意长的字符,因而可以造成堆溢出。

首先要处理怎样完成地点泄漏,平常来讲经由历程建立函数可以获得堆地点,然则怎样获得libc的地点?答案是可以经由历程请求大的堆块,请求堆块很大时,mmap出来的内存堆块会紧贴着libc,可经由历程偏移获得libc地点。从下图中可以看到,当请求堆块大小为0x200000时,请求出来的堆块紧贴libc,可经由历程堆块地点获得libc基址。
IO FILE 之挟制vtable及FSOP

怎样获得unsorted bin?想要应用unsorted bin attack完成_IO_list_all的改写,那末就须要有unsorted bin才行,只要一个堆块,怎样获得unsorted bin?答案是应用top chunk不足时堆的分派的机制,当top chunk不足以分派,体系会分派新的top chunk并将之前的chunk 运用free函数开释,此时会将堆块开释至unsorted bin中。我们可以应用掩盖,捏造top chunk的size,开释的堆块需满足下述前提:

assert ((old_top == initial_top (av) && old_size == 0) ||
          ((unsigned long) (old_size) >= MINSIZE &&
           prev_inuse (old_top) &&
           ((unsigned long) old_end & (pagesize - 1)) == 0));

即:

  1. size须要大于0x20(MINSIZE)
  2. prev_inuse位要为1
  3. top chunk address + top chunk size 必需是页对齐的(页大小平常为0x1000)

终究应用unsorted bin attack,将_IO_list_all指向main_arenaunsorted_bins数组的位置。

此时的_IO_list_all因为指向的时main arena中的地点,并非完整可控的。
IO FILE 之挟制vtable及FSOP

然则它的chain字段倒是可控的,因为我们可以经由历程捏造一个大小为0x60的small bin开释到main arena中,从而在unsorted bin attack后,该字段恰好被看成_chain字段,以下图所示:
IO FILE 之挟制vtable及FSOP

当挪用_IO_flush_all_lockp时,_IO_list_all的头节点并不会使得我们可以掌握实行流,然则当经由历程fp = fp->_chain链表,对第二个节点举行革新缓冲区的时刻,第二个节点的数据就是完整可控的。我们就可以捏造该组织体,组织好数据以及vtable,在挪用vtable中的_IO_OVERFLOW函数时完成对实行流的挟制。

写exp时,可以应用pwn_debugIO_FILE_plus模块中的orange_check函数来搜检当前捏造的数据是不是满足house of orange的进击,以及运用show函数来显现当前捏造的FILE组织体。

捏造的IO FILE组织以下:
IO FILE 之挟制vtable及FSOP

可以看到_mode为0,_IO_write_ptr也大于fp->_IO_write_base因而会触发它的_IO_OVERFLOW函数,它的vtable被全都捏造成了system的地点,以下图所示:
IO FILE 之挟制vtable及FSOP

终究实行system("bin/sh")拿到shell。

小结

vtable挟制和FSOP照样比较好明白的,下一篇将引见vtable check机制和它的绕过要领。

pwn_debug新增了一个模块IO_FILE_plus,该模块可以很轻易的检察和组织IO FILE组织体,以及搜检组织体是不是满足应用前提。本文中可以运用的api为IO_FILE_plus.orange_check,即搜检当前组织的IO FILE是不是满足house of orange的进击前提。

exp和相干文件在我的github

参考链接

  1. unsorted bin attack剖析
  2. 捏造vtable挟制顺序流程


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明IO FILE 之挟制vtable及FSOP
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址