Heap 0x02

Luo's Blog / 2023-05-18 / 原文

这篇我估计大概能写完堆溢出和off-by-one,铸币堆的漏洞利用是真踏马的看不懂的不知所措的难

Heap overflow

堆溢出是指程序向某个堆块中写入的字节数超过了堆块本身可使用的字节数,之所以说是可使用而不是用户申请的字节数,是因为堆本身各种地方都会有很多奇妙的操作(规范一点的说是因为堆管理器会对用户所申请的字节数进行调整),从而出现了类似于栈溢出一样的结果,即数据溢出并覆盖了物理相邻的高地址的下一个堆块的内容

但是与栈溢出很不一样的就是直接利用堆溢出并非能做到和栈溢出一样的效果,因为堆上不存在返回地址等等能让我们直接控制执行流的这么些有用的数据,所以这种缓冲区溢出在我看来更像一种利用层面的手段/途径,具体到方法层面还需要在机制层面的巧妙利用(如unlink机制,特殊的溢出off-by-one等等),堆溢出算是一种基础

写实际的漏洞利用啥的之前呢,我先还笔债来写一下unlink机制:

unlink是用来从bins中取出chunk的操作,我的理解是从各种bins中取出chunk的操作较为频繁,而且涉及到链表指针各种变换,总体如果用函数实现就会很繁琐,所以unlink实际上被单独实现为一个宏的形式,如下:

/* Take a chunk off a bin list */
#define unlink(AV, 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, AV);  \
    else {								      
        FD->bk = BK;							      
        BK->fd = FD;							      
        if (!in_smallbin_range (P->size)				      
            && __builtin_expect (P->fd_nextsize != NULL, 0)) {		      
	    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);					      
            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;		      
              }								      
          }								      
      }									      
}

用一张图描述一下这个过程,图来自CTFwiki:
image

为什么说unlink这个操作使用的多呢,因为很多操作实质上都是unlink去做到的(怎么感觉有点废话

  • malloc,从large bin中拿chunk
  • free,合并chunk
  • realloc等等,不穷举

然后是unlink的漏洞利用层面,写这个博客的时候还没有怎么理解这里的漏洞利用方面,看看后续做题再巩固一下这个知识层面,在这里我就只挂个图(CTFwiki的)
image

这是unlink的机制和一些操作,然后我们说回堆溢出

有关堆溢出

取了这么一个标题,其实是看CTFwiki有什么我就记下来什么,方便我以后往回看(其实已经出现了往回复习那些宏的过程,感觉堆就是很繁琐,要记很多东西,也可能是我太菜了

这里看CTFwiki的东西其实和栈溢出那些差不多,我放一下我没怎么见过的realloc和calloc函数:

#include <stdio.h>

int main(void) 
{
  char *chunk,*chunk1;
  chunk=malloc(16);
  chunk1=realloc(chunk,32);
  return 0;
}

先是realloc,这个函数并非只是去修改大小那么简单:

  • 如果申请的size>原size,

    • 如果与topchunk相邻,直接扩展
    • 如果不相邻,则相当于free+malloc
  • 如果小于原size

    • 如果两者差值小于最小chunk(补习一下,MIN_CHUNK_SIZE 即chunk header+fd*bk,32位0x10,64位0x20)无影响
    • 若大于等于,则切割,然后free那个没用到的部分
  • 如果realloc后面那个参数是0,就等同于free

  • 如果两者大小相同就不进行操作(?

23.5.13,先写到这..
(23.5.18:offbyone有点难,先去看UAF漏洞了,后续再补)