教练,我还想用IO

教练,我还想用IO

十一月 02, 2019

IO的利用在现在的pwn题里越来越常见夜越来越多了,虽然house of orange已经是angelboy大神在hitcon 2016里提出的了,但是现在还是很常用,这里吐槽一下hitcon2019还是很变态2333,鉴于现在的使用越来越多,这里打算重新学习一下IO,鉴于水平问题,所不及之处或错误之处还望海涵并与我联系,因为之前已经写过一篇IO的,所有对IO不了解的可以先看一下我之前的文章
_IO_FILE结构体利用

这里贴两个链接,以表自己对angelboy师傅的膜拜

1
2
3
http://4ngelboy.blogspot.com/2016/10/hitcon-ctf-qual-2016-house-of-orange.html

http://blog.angelboy.tw/

house of orange再浅析

house of orange的使用条件

  1. 可以泄露heap和libc基址
  2. 可以触发unsorted bin attack
  3. 有空间让我们伪造FILE结构体

而条件一可以使用unsortedbin和small bin来泄露

原题的难点在于没有提供free函数,而在这种情况下我们该如何制造一个unsorted bin就是难以解决的问题了

解决方法

关于第一个其实有一个小trick,也是一个小机制

我们知道我们malloc块的时候,一般都是在top chunk处切一小块下来给我们,但是当我们申请的大小大于top chunk的size且不会使用mmap拓展(即仅仅是brk)的时候,就会把现在的top chunk变成unsorted bin chunk,此时我们就可以利用这个unsorted bin来泄露了

剩下的就是我们该如何伪造FILE结构体了

而我们知道通过unsorted bin attack 可以向任意地址写如main_arena结构体中的top对应地址值

struct main_arena:

1
2
3
4
5
6
static struct malloc_state main_arena =
{
.mutex = _LIBC_LOCK_INITIALIZER,
.next = &main_arena,
.attached_threads = 1
};

这里就先到此为止,下面我们进入正题,IO的利用

IO来了

我们知道我们日常使用的一个代码比如:

1
FILE *fd=fopen('');

此时系统并不会直接给fd分配一个_IO_FILE指针,其实他分配给FD的是_IO_FILE_plus结构体,

这里也贴一下方便查看

1
2
3
4
5
struct _IO_FILE_plus
{
_IO_FILE file;
IO_jump_t *vtable;
}

而我们的vtable虚表的结构是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void * funcs[] = {
1 NULL, // "extra word"
2 NULL, // DUMMY
3 exit, // finish
4 NULL, // overflow
5 NULL, // underflow
6 NULL, // uflow
7 NULL, // pbackfail

8 NULL, // xsputn #printf
9 NULL, // xsgetn
10 NULL, // seekoff
11 NULL, // seekpos
12 NULL, // setbuf
13 NULL, // sync
14 NULL, // doallocate
15 NULL, // read
16 NULL, // write
17 NULL, // seek
18 pwn, // close
19 NULL, // stat
20 NULL, // showmanyc
21 NULL, // imbue
};

这里要特地提一下,house of orange这道题的环境还是libc2.23,也就意味着我们可以直接更改vtable来改变程序的执行流程hhh

而我们如何跳到vtable_IO_jump_t执行呢,这也很简单,只要构造一个错误就好,我们可以通过unsorted bin attack 来使得malloc出错,从而调用malloc_printerr,然后的调用链如下所示:

1
malloc_printerr->__libc_message->abort->fflush(_IO_flush_all_lockp)->vtable->_IO_OVERFLOW(hijack->system)

此时就可以完成整个调用链来获得shell
而且此时有两种绕过check的构造方式

1
2
3
4
5
6
1.fp->_mode <= 0
2.fp->_IO_write_ptr > fp->_IO_write_base

1._IO_vtable_offset (fp) == 0
2.fp->_mode > 0
3.fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base

libc>2.23

方法一

之前的文章也有提及,在libc>2.23之后,glibc就添加了一个新的check函数(_IO_vtable_check)

虽然我们不能随意伪造一个新的结构了,但是libc段上的__libc_IO_vtables还是可以用的,那么我们,而我们只需要更改_IO_str_jumps的虚表_IO_str_overflow即可,然后如下构造即可:

1
2
3
4
5
_flags = 0
_IO_write_ptr = 0x7fffffffffffffff
_IO_write_base = 0
_IO_buf_end = (binsh_addr-100)/2
_IO_buf_base = 0

方法二

这个方法来自V神的思考:即修改(_IO_str_finish)链接

1
2
3
4
5
6
7
8
9
void
_IO_str_finish (_IO_FILE *fp, int dummy)
{
if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF)) //唯一需要bypass的条件
(((_IO_strfile *) fp)->_s._free_buffer) (fp->_IO_buf_base);// getshell , [fp+0xe8]
fp->_IO_buf_base = NULL;

_IO_default_finish (fp, 0);
}

利用stdout泄露地址

在没有输出函数的情况下,我们该如何泄露地址呢?

我们只需要劫持stdout指针即可
一般来说是通过UAF,接着改FD的main_arena+88的末位
如果没有则利用攻击global_max_fast的方式去做,然后利用fastbin attack,变成stdout-xx的位置(得有0x7f或者0xff的size,0x7f在0x43的位置,0xff在0x51的位置),下一次申请时就可以从上往下写,改写flag标志位为0xfbad1800固定值,同时修改IO_Write_base末尾为’\x00’,在flag位和IO_Write_base位之间填写的东西可以为任意值,我们的目的是下溢改写IO_Write_base

写到这里就差的不多结束了,因为发现了一篇写的更优秀的文章,为了避免重复造轮子,这里贴出来https://www.anquanke.com/post/id/168802#h2-9