Safe Linking机制分析及绕过

glibc源码分析

在最开始简单的回顾一下fast bin和tcache的管理机制。首先假设fast bin中存在以下链表:


当一个新的,符合条件的chunk被free之后,则会插入到链表的头部,就如下图:

在旧版本的libc中缺少对于fd/next指针的保护,导致只要控制这个指针,就能够造成任意地址读写。而Safe Linking的机制就是对这个指针进行加密保护。
Safe Linking的实现用到两个宏,一个用于加密,一个用于解密,相关定义如下:

/* Safe-Linking:
   Use randomness from ASLR (mmap_base) to protect single-linked lists
   of Fast-Bins and TCache.  That is, mask the "next" pointers of the
   lists' chunks, and also perform allocation alignment checks on them.
   This mechanism reduces the risk of pointer hijacking, as was done with
   Safe-Unlinking in the double-linked lists of Small-Bins.
   It assumes a minimum page size of 4096 bytes (12 bits).  Systems with
   larger pages provide less entropy, although the pointer mangling
   still works.  */
#define PROTECT_PTR(pos, ptr) \
  ((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
#define REVEAL_PTR(ptr)  PROTECT_PTR (&ptr, ptr)

直接看可能有点难理解,我们先从调用开始看,下面是tcache_put对于这个宏的调用:

/* Caller must ensure that we know tc_idx is valid and there's room
   for more chunks.  */
static __always_inline void tcache_put(mchunkptr chunk, size_t tc_idx) {
  tcache_entry *e = (tcache_entry *)chunk2mem(chunk);

  /* Mark this chunk as "in the tcache" so the test in _int_free will
     detect a double free.  */
  e->key = tcache;

  e->next = PROTECT_PTR(&e->next, tcache->entries[tc_idx]);
  tcache->entries[tc_idx] = e;
  ++(tcache->counts[tc_idx]);
}

以及free函数中,对于这个宏调用相关的定义如下

static void _int_free(mstate av, mchunkptr p, int have_lock) {
  ...
    unsigned int idx = fastbin_index(size);
    fb = &fastbin(av, idx);

    /* Atomically link P to its fastbin: P->FD = *FB; *FB = P;  */
    mchunkptr old = *fb, old2;

    if (SINGLE_THREAD_P) {
      /* Check that the top of the bin is not the record we are going to
         add (i.e., double free).  */
      if (__builtin_expect(old == p, 0))
        malloc_printerr("double free or corruption (fasttop)");
      p->fd = PROTECT_PTR(&p->fd, old);
      *fb = p;
    } else
      do {
        /* Check that the top of the bin is not the record we are going to
           add (i.e., double free).  */
        if (__builtin_expect(old == p, 0))
          malloc_printerr("double free or corruption (fasttop)");
        old2 = old;
        p->fd = PROTECT_PTR(&p->fd, old);
      } while ((old = catomic_compare_and_exchange_val_rel(fb, p, old2)) !=
               old2);

    /* Check that size of fastbin chunk at the top is the same as
       size of the chunk that we are adding.  We can dereference OLD
       only if we have the lock, otherwise it might have already been
       allocated again.  */
    if (have_lock && old != NULL &&
        __builtin_expect(fastbin_index(chunksize(old)) != idx, 0))
      malloc_printerr("invalid fastbin entry (free)");
  ...
}

根据实现以及调用时的上下文可以看出,就是Chunk3的fd/next的地址与Chunk2的fd/next地址进行位移和异或操作。宏里面的pos对应新的chunk的fd/next地址,也就是对应图中的Chunk3;ptr对应旧的chunk的fd/next地址,也就是对应图中的Chunk2。通过pos右移12位,再与ptr异或实现加密。(pos >> 12可以视为异或加密的key)

这里的右移12位与内存的分页机制有关。

因为是异或加密,因此解密只需要再次运行加密流程即可。

绕过方法

在Linux中堆(没有经过扩展)和其他段一样,都是有一个基地址,获取到基地址就可以通过偏移获取到chunk的地址,也就是说,只要能够泄漏基地址,就可以获取key的值,从而实现攻击。
然后将加密后的地址写入fd/next部分即可实现攻击。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇