Day5 - House of Einherjar

House of Einherjar这个技术可以强制使得malloc返回一个几乎任意地址的chunk。其主要在于滥用free中的后向合并操作(合并低地址的chunk),这样可以避免碎片化。

在一些特殊大小的堆块中,off by one不仅可以修改下一个堆块的prev_size,还可以修改下一个堆块的PREV_INUSE位。

参考

CTF wiki

原理

后向合并操作

free函数中的后向合并核心操作如下

1
2
3
4
5
6
7
/* consolidate backward */
if (!prev_inuse(p)) {
prevsize = prev_size(p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);
}
  • 两个物理相邻的chunk会共享prev_size字段,当低地址的chunk处于使用状态时,相邻的高地址chunk的该字段便可以被低地址的chunk使用。因此,我们阔以通过写低地址chunk来覆盖高地址chunk的prev_size字段
  • 一个chunk的PREV_INUSE位标记了其物理相邻的低地址chunk的使用状态
  • 后向合并时,新的chunk位置取决于chunk_at_offset(p, -((long) prevsize))

这样的话,如果我们控制一个chunk的prev_size和PREV_INUSE字段,那么我们就能将新的chunk指向几乎任何位置。

利用

利用主要需要注意:

  • 需要在目的位置构造好fake chunk,而且要绕过unlink的检查

    1
    2
    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      \
    malloc_printerr ("corrupted double-linked list");

    可以让FD = p,BK = p。这样FD->bk == p而且BK->fd == p

  • unlink还有一个检查是检查fake chunk的大小

    1
    2
    if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      \
    malloc_printerr ("corrupted size vs. prev_size");

    对fake chunk的size和prev_size的验证只有在unlink里验证,这样只要保证fake chunk的next chunk的prev_size跟fake chunk的size相同就行。

总结:

  • 需要可以有写物理相邻高地址的prev_size和PREV_INUSE部分
  • 需要计算目的chunk与p1地址之间的差值,所以需要泄露地址
  • 要在目的chunk附近构造相应fake chunk,用来绕过unlink检测

栗子

2016 Seccon tinypad

首先康康保护

1
2
3
4
5
6
7
8
$ checksec tinypad 
[!] Couldn't find relocations against PLT to get symbols
[*] '/home/critiz/Desktop/tinypad'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE