近期又出现一个新的关于IO_FILE
的利用方式,不过原作者并没有出名字但是我blog又是用house of系列分类的所以就先随便取个名字。
原理分析
此次聚焦的vtable表为:
1 | /* the jump table. */ |
主要利用的结构体为:
1 | struct _IO_obstack_file |
也就是在IO_FILE
结构体下加一个obstack
结构体指针。下面则是obstack
结构体的定义:
1 | struct obstack /* control current object in current chunk */ |
可以看到,上述的vtable中只有_IO_obstack_overflow
、_IO_obstack_xsputn
这样两个函数,首先关注前一个
_IO_obstack_overflow
1 | static int |
可以看到其中存在一个assert,注意下面如果我们走exit这条路来清空所有缓存时触发的话就会出现rsi必定为-1
的情况,所以此路不通
_IO_obstack_xsputn
1 | static size_t |
这里我们的目标时调用到obstack_grow
函数,所以我们需要进入这个if语句,这一点很好说,如果我们可以控制这个结构体就可以非常轻松的控制这里的值而后进入if语句,随后又会执行obstack_blank_fast
函数
1 | #define obstack_blank_fast(h, n) ((h)->next_free += (n)) |
这个其实是一个宏定义,可以看到内部其实不会特别影响后续的内容。在继续执行就会进入我们期望的函数:
1 |
可以看到这个也是一个宏定义,同样的我们又必须通过_o->next_free + __len > __o->chunk_limit
这条if语句才能调用到_obstack_newchunk
函数。
1 | void |
可以看到这里可以直接调用到我们期望的宏定义CALL_CHUNKFUN
:
1 |
可以看到这里存在直接拿指针当作函数的操作(*(h)->chunkfun)((h)->extra_arg, (size))
,条件也就是(((h)->use_extra_arg)
不为0;
总结
所以从上到下的调用链也是可以直接写出来了:
_IO_obstack_xsputn
=>obstack_grow
=>_obstack_newchunk
=>CALL_CHUNKFUN
=>(*(h)->chunkfun)((h)->extra_arg, (size))
最后再根据结构体属性的偏移写上上述约束的值即可。
当_IO_list_all
指向我们可控A地址时,我们需要对A地址写入如下数据:
1 | A + 0x18 = 1; |
poc
1 |
|
以上libc均使用:Ubuntu GLIBC 2.36-0ubuntu4
参考文章