【PWN】House Of Einherjar&House Of Force

ctfwiki上house系列第一个与第二个。利用方法也相对简单,内容也较少,决定一块儿复习了。

House Of Einherjar

House Of Einherjar个人理解,通过修改victim堆块的pre_size和pre_inuse,free操作会检查前后已经被释放的堆块,并对都释放了的堆块进行合并操作,如果能够有效伪造堆块fake_chunk,可以让victim块和fake_chunk均伪造为释放状态,最后合并成一个特别大的块,再次申请来某片空间的控制权。

由于extend的操作,其不得不与unlink打交道,这里就先补一下unlink的知识吧,弥补一下没有出专题。

libc2.29及以后增加了对presize和size位的检测

因为我们往往无法修改size,最多进行off-by-one的覆盖,ctfwiki中提到了一种绕过方式,在bk位写size,利用largebin的fd_nextsize与bk_nextsize位绕过unlink检测,创建一个fake chunk。

下面说一下具体的方法:

我们可以控制 fd_nextsize 指向堆上的任意地址,可以容易地使之指向一个 fastbin + 0x10 - 0x18,而 fastbin 中的 fd 也会指向堆上的一个地址,通过部分覆写该指针也可以使该指针指向之前的 large bin + 0x10,这样就可以通过 fd->bk == p 的检测。

由于 bk_nextsize 我们无法修改,所以 bk->fd 必然在原先的 large bin chunk 的 fd 指针处(这个 fd 被我们破坏了)。通过 fastbin 的链表特性可以做到修改这个指针且不影响其他的数据,再部分覆写之就可以通过 bk->fd==p 的检测了。

这是ctfwiki的原话,还是分析例题看看他究竟是如何解决的吧

对ctfwiki的示例三的步骤可以这样总结

1.构造偏移。由于tcache的结构会保存在堆中,所以初始时malloc得到的chunk的地址必不可能与0x1000对齐。在多次malloc后,人为实现chunk a的低地址与0x1000对齐,但这还不够,由于此题的off-by-one,导致会对第二个字节也覆盖为0,所以不方便设置偏移指向需要的chunk,为此,随机地址有1/16的概率,实现0x10000对齐,这样一来,方便我们下面的利用。

2.设置假chunk,使fd(fd_nextsize)指向下面的chunk的fd向前偏移0x18的位置。free后再分配,形成多块,在第一个chunk(保留fd_nextsize与bk_nextsize)内伪造chunk。

3.free掉第一个chunk和指向的chunk至fastbin(因为tcache会将key位重置,毁坏伪造的size)

4.部分覆盖第一个chunk与指定chunk的fd位,完成对unlink的绕过。之后就可以overlapping了。

接着说House Of Einherjar,看了一下之前自己写的关于how2heap的说明,感觉不太准确,这里再细说一下如何利用。其实大体和overlapping的利用是极其类似的。

1.伪造chunk。如how2heap,这个chunk可以是在栈上的。

2.设置fake chunk的fd与bk,绕过unlink,如果是2.28版本以后,还要伪造size

3.通过off-by-one,设置某chunk的同时把其pre in use置0

4.free掉该chunk,实现extend。在此分配,就可以得到想要的地址了。

核心利用点

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

由于(long)所以不用担心(b-sizeof(size_t)*2) - (uint8_t*)fake_chunk)最后是个负数。ctfwiki的例题其中有个关键利用就是伪造了chunk后,可以实现不仅对该chunk,还包括后面地址的读写能力,因此对某些特殊题有奇用。是个很有意思的利用。

例题待补充

House Of Force

利用前提,能够修改top chunk的size

漏洞点:

// 获取当前的top chunk,并计算其对应的大小
victim = av->top;
size   = chunksize(victim);
// 如果在分割之后,其大小仍然满足 chunk 的最小大小,那么就可以直接进行分割。
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;
}
#define chunk_at_offset(p, s) ((mchunkptr)(((char *) (p)) + (s)))

这也就意味着,当我们想分配到比堆小的地址时,可以写入一个负数的偏移,达到top chunk地址甚至减小的效果。

ctfwiki在讲述第一种利用时,提到了一个非常重要的地方

/*
   Check if a request is so large that it would wrap around zero when
   padded and aligned. To simplify some other code, the bound is made
   low enough so that adding MINSIZE will also not wrap around zero.
 */

#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 */
//MALLOC_ALIGN_MASK = 2 * SIZE_SZ -1
#define request2size(req)                                                      \
    (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)                           \
         ? MINSIZE                                                             \
         : ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

/*  Same, except also perform argument check */

#define checked_request2size(req, sz)                                          \
    if (REQUEST_OUT_OF_RANGE(req)) {                                           \
        __set_errno(ENOMEM);                                                   \
        return 0;                                                              \
    }                                                                          \
    (sz) = request2size(req);

一方面,我们需要绕过 REQUEST_OUT_OF_RANGE(req) 这个检测,即我们传给 malloc 的值在负数范围内,不得大于 -2 * MINSIZE,这个一般情况下都是可以满足的。

另一方面,在满足对应的约束后,我们需要使得 request2size正好转换为对应的大小,也就是说,我们需要使得 ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK 恰好为 - 4112。首先,很显然,-4112 是 chunk 对齐的,那么我们只需要将其分别减去 SIZE_SZ,MALLOC_ALIGN_MASK 就可以得到对应的需要申请的值。其实我们这里只需要减 SIZE_SZ 就可以了,因为多减的 MALLOC_ALIGN_MASK 最后还会被对齐掉。而如果 -4112 不是 MALLOC_ALIGN 的时候,我们就需要多减一些了。当然,我们最好使得分配之后得到的 chunk 也是对齐的,因为在释放一个 chunk 的时候,会进行对齐检查。

虽然很关键但上述条件往往被忽略。

第二种利用则是正常向高地址修改。

利用所需条件

  • 首先,需要存在漏洞使得用户能够控制 top chunk 的 size 域。
  • 其次,需要用户能自由控制 malloc 的分配大小
  • 第三,分配的次数不能受限制

今天比较晚了,来不及再做题,例题待补充

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值