0x00 前言
主要是一道house of orange,利用方法很经典,学习了
0x01 题目分析
main
1 | void __fastcall main(__int64 a1, char **a2, char **a3) |
其中定义了两个int_64[8]数组在BSS段,分别存储书页的地址和书页内容大小信息,两数组在BSS段上位置相邻。
add
添加的操作顺序从0到8依次搜索,判断标准是该位置是否为空,并获得一次向堆地址写的机会
1 | int add() |
view
利用%s输出信息,可以用来泄露libc和heap信息
1 | int view() |
edit
编辑操作,可以输入0到7的数值,用strlen重新确定大小
1 | int sub_400B27() |
info
输出名称信息
1 | unsigned __int64 info() |
0x02 漏洞分析
- 首先注意到name所在bss段位置与存储heap信息的bss段相邻,因此可以用info泄露heap地址
- eidt的时候是用strlen重新计算size的,因此两次edit后可以覆盖到下一个堆块的size位置
- add的时候多add了一个,应该是0到7就可以。利用的话就是堆地址会写到heap_page[0]的size位置,这样就可以造成很长的堆溢出
0x03 漏洞利用
整个程序都没有出现free,house of orange的典型利用场景。
利用条件
- 泄露heap,libc地址
- 堆溢出
- libc2.23 及以下版本
泄露信息(libc地址)
因为程序中有堆的越界写,可以修改top_chunk
的大小。在malloc
源码里面如果申请的堆块大小超过了top_chunk
的大小,将调用sysmalloc
来进行分配。sysmalloc
里面针对这种情况有两种处理,一种是直接mmap
出来一块内存,另一种是扩展top_chunk
1 | /* |
就是如果申请大小>=mp_.mmap_threshold
,就会mmap
。我们质只要申请不要过大,一般不会触发这个,这个mmap_threshold
的值为128*1024
。
不过下面还有两个assert
需要检查,如下
1 | old_top = av->top; |
第一个assert就是要求修改后的top_chunk_size
必须满足
top_chunk
的size必须大于MINSIZE
top_chunk
的pre_inuse位必须为1- 让
top_chunk
满足页对齐,一般就是后三位都为0 top_chunk
的size小于申请的大小
满足以上四个条件之后,继续往下执行最后把原先的那个old_top给释放掉了,进入unsortedbin
中
这样的话我们再次申请一个堆块分配到这块区域中就能泄露libc地址了
劫持控制流
我们知道有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
里面的结构,采用的是虚表调用的方式。
这里也学到一个操作,调试这里的时候可以直接b _IO_flush_all_lockp
来断到这个函数,然后看自己的fsop是不是成功了,也可以直接用fsop这个命令。最后看第二个fp是不是unsortedbin的地址,还有就是Func是不是自己想要跳转的函数
其中使用到了IO_FILE对象中的虚表,如果我们能够修改IO_FILE的内容那么就可以一定程度上劫持流程。
IO_FILE_ALL是一个指向IO_FILE_plus的结构指针,结构如下图所示,具体结构不需要太了解清晰,大概懂一些也就行。
那么怎么劫持呢,这里又需要用到unsortbin attack
的知识。unsortbin attack
是怎么一回事呢,其实就是在malloc的过程中,unsortbin会从链表上卸下来(只要分配的大小不是fastchunk大小),只要我们能控制unsortedbin的bk内容,在unlink的时候就会把unsortedbin的地址写入bk+0x10的位置
这样我们把_IO_list_all
的地址改成main_arena
,但是main_aren
上的内容我们也不是完全可控的,于是就是这个利用很精妙的地方,让其通过chain
跳转到我们能自己控制内容的地方即伪造的FILE结构
这里还是要牵扯到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
大小。
1 | while (fp != NULL) |
下一步就是在原top内伪造_IO_file_plus结构体,满足
fp->mode>=0
_IO_vtable_offset (fp) ==0
fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
即可,构造的结构体如下:(不小心按错退出了,与原先的top地址有变化)
最终再malloc一个块,触发即可
0x04 EXP
1 | from pwn import * |
0x05 参考
http://p4nda.top/2017/12/15/pwnable-tw-bookwriter/
https://bbs.pediy.com/thread-222718.htm