Unlink原理和一些手法

unlink · 浏览次数 : 0

小编点评

**unlink原理** unlink 是利用 glibc 的内存回收机制造成攻击的。当两个free的堆块在物理上相邻时,会将他们合并,并将原来free的堆块在原来的链表中解链,加入新的链表中。 **一些方法** * **检查 p 和其前后的 chunk 是否构成双向链表** 如果 `fd > bk` 且 `BK > fd`,则说明 p 和其前后的 chunk构成双向链表。我们可以使用 `malloc_printerr` 打印错误信息。 * **绕过检查** 由于 `unlink` 加上了保护机制,我们无法像以前那样直接修改 `fd` 和 `bk` 指针。但是,我们可以利用以下技巧绕过检查: 1. 将 `fd` 设置为 `*p - 0x18`。 2. 将 `bk` 设置为 `*p - 0x10`。 3. 修改 `magic` 处地址的值为理想的值。 4. 选择隐藏选项,得到 `flag`。 * **伪造修改** 1. 创建 4 个较大的 chunk。 2. 编辑 chunk 进行伪造修改 `bk` 和 `fd` 指针。 3. 修改 `magic` 处地址的值为理想值。 4. 选择隐藏选项,得到 `flag`。

正文

Unlink原理和一些手法

✅简单介绍一下unlink相关的知识

unlink是利用glibc malloc 的内存回收机制造成攻击的,核心就在于当两个free的堆块在物理上相邻时,会将他们合并,并将原来free的堆块在原来的链表中解链,加入新的链表中其目的是把一个双向链表中的空闲块拿出来(例如 free 时和目前物理相邻的 free chunk 进行合并)比如说数组这类的.........等等。

前提是我们可以进行溢出修改下一个chunk的fd和bk指针

当我们实现unlink的时候就可以任意地址写,但是现实是残酷的,现在的unlink加了很多保护,我们先看一下定义。

#define unlink(P, BK, FD) {                                            
  FD = P->fd;                                                          
  BK = P->bk;                                                          
  if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                
    malloc_printerr (check_action, "corrupted double-linked list", P); 
  else {                                                              
    FD->bk = BK;                                                       
    BK->fd = FD;                                                       
    if (!in_smallbin_range (P->size)                       
    && __builtin_expect (P->fd_nextsize != NULL, 0)) {         
      assert (P->fd_nextsize->bk_nextsize == P);              
      assert (P->bk_nextsize->fd_nextsize == P);              
      if (FD->fd_nextsize == NULL) {                       
    if (P->fd_nextsize == P)                       
      FD->fd_nextsize = FD->bk_nextsize = FD;              
    else {                                 
      FD->fd_nextsize = P->fd_nextsize;                
      FD->bk_nextsize = P->bk_nextsize;                
      P->fd_nextsize->bk_nextsize = FD;                
      P->bk_nextsize->fd_nextsize = FD;                
    }                                  
      } else {                                 
    P->fd_nextsize->bk_nextsize = P->bk_nextsize;             
    P->bk_nextsize->fd_nextsize = P->fd_nextsize;              
      }                                    
    }                                      
  }                                                                    
}

大多是一些保护,其中最重要的是下面这个

//检查p和其前后的chunk是否构成双向链表
if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
    malloc_printerr ("corrupted double-linked list");

这是一个关键 check ,那么如何绕过检查呢?

64位满足以下式子32位依次类推:

P->fd->bk == P <=> *(P->fd + 0x18) == P 
p->bk->fd == P <=> *(p->bk + 0x10) == P

那么我们可以将fd设置为*p-0x18 bk设置为*p-0x10,那么我们就可以绕过检查,那么当我们free下一个chunk的时候就会进行合并,实现了unlink,那么当我们再次修改该chunk的时候指针就会指向*p-0x18的位置进而实现任意地址写。

我们通过题目看一下具体用法题目链接🔗https://pan.baidu.com/s/1lEba9fl8Yt56MvC6fj3xjQ?pwd=w6du提取码:w6du

首先看保护

got表可修改,我们ida载入看一下

是一些菜单什么的,其中有一个隐藏选项,看一下

好家伙是后门函数,但是他有条件,magic处地址的值必须大于等于114514才可以执行

值得注意的是edit函数没有对我们输入的修改长度进行检查,那么我们可以使用unlink修改magic地址的值

✅思路:

1.先申请4个较大的chunk(方便到时候我们进行伪造)4个chunk是因为*p-0x18可以指向第一个chunk,最后一个用来free

2.编辑chunk进行伪造修改bk和fd指针,实现unlink

3.修改magic处地址的值为理想的值,选择隐藏选项得到flag

先贴一下wp我再详细解释

 

这里采用下标为3的数组是因为当设置完bk和fd之后会指向第一个下标(*p-0x18),那么我们再次修改下标为0的chunk数据是理想数据下标为3的数组为magic地址那么

相当于magic的地址为我们理想的值(3->0->magic)中间把下标为3的chunk分割成了两个chunk,伪造fd和bk以及下一个chunk的头部使当前chunk为free状态,那么再free下一个chunk就可以完成unlink,最后再进行修改就好啦具体可以参考这个图和这个链接https://blog.csdn.net/qq_35493457/article/details/105857572

与Unlink原理和一些手法相似的内容: