堆技巧 unlink

原理

  我们在利用 unlink 所造成的漏洞时,其实就是对 chunk 进行内存布局,然后借助 unlink 操作来达成修改指针的效果。

  假设现在有物理空间连续的两个 chunk(Q,Nextchunk),其中 Q 处于使用状态、 Nextchunk 处于释放状态。那么如果我们通过某种方式将 Nextchunk 的 fd 和 bk 指针修改为指定的值。

prev_size    chunk Q    
size = 0x81    
User content
prev_size    Nextchunk  
size = 0x80
fd = target addr -12
bk = expect value

  当我们 free(Q) 时,进行如下操作

  • glibc 判断这个块是 small chunk
  • 判断向前合并,发现前一个 chunk 处于使用状态,不需要合并。
  • 判断向后合并,发现后一个 chunk 处于空闲状态,需要合并。
  • 继而对 Nextchunk 采取 unlink 操作。
    • FD = P->fd = target addr - 12
    • BK = P->bk = expect value
    • FD->bk = BK ,即 *(target addr -12 + 12) = expect value
    • BK->fd = FD ,即 *(expect value + 8) = FD = target addr - 12

  可以看到,我们可以通过 unlink 实现任意地址读写的目的,但是我们还是需要确保 expect value + 8 地址具有可写的权限。
  比如我们将 target addr 设置为某个 got 表项,那么当程序调用对应的 libc 函数时,就会直接执行我们设置的 expect value 处的代码。但是需要注意,expect value + 8 处的值被破坏了,需要想办法绕过。

  现在的 unlink 都有如下检查

// 由于 P 已经在双向链表中,所以有两个地方记录其大小,所以检查一下其大小是否一致(size检查)
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))      
      malloc_printerr ("corrupted size vs. prev_size");               
// 检查 fd 和 bk 指针(双向链表完整性检查)
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                      
  malloc_printerr (check_action, "corrupted double-linked list", P, AV);  

  // largebin 中 next_size 双向链表完整性检查 
              if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0)              
                || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))    
              malloc_printerr (check_action,                                      
                               "corrupted double-linked list (not small)",    
                               P, AV);

  可以发现,上述的利用方法绕不过双向链表完整性检查。我么需要通过伪造的方式绕过这一判断。
  首先我们通过覆盖,将 nextchunk 的 FD 指针指向了 fakeFD ,将 nextchunk 的 BK 指针指向了 fakeBK。那么为了通过验证,我们需要:

  • fakeFD -> bk == P ,即 *(fakeFD + 12) == P
  • fdkeBK -> fd == P ,即 *(fakeBK + 8) == P

  当满足上述两式时,可以进入 Unlink 的环节,进行如下操作:

  • fakeFD -> bk == fakeBK ,即 *(fakeFD + 12) = fakeBK
  • fakeBK -> fd == fakeFD ,即 *(fakeBK + 8) == fakeFD

  所以为了绕过检测,我们只需要按如下方式修改

prev_size    chunk Q    
size = 0x81    
User content
prev_size    Nextchunk  
size = 0x80
fd = nextchunk addr - 12
bk = nextchunk addr - 8

  这样在 unlink 后,可以使 p 的指针指向比自己低 12 的地址处。

利用思路

条件

  • Use after free ,可以修改 free 状态下 smallbin 或是 unsorted bin 的 fd 和 bk 指针
  • 已知位置存在一个指针指向可进行 UAF 的 chunk。

效果

  使得已指向 UAF chunk 的指针 ptr 变为 ptr - 0x18

思路

  设指向可 UAF chunk 的指针的地址为 ptr

  • 修改 fd 为 ptr - 0x18
  • 修改 bk 为 ptr - 0x10
  • 触发 unlink

  此时 ptr 处的指针会变成 ptr - 0x18。

原文地址:https://www.cnblogs.com/luoleqi/p/12369065.html