堆溢出----Unlink

xiaoxiao2022-06-12  45

学习资料:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/unlink/

                  http://www.anquan.us/static/drops/tips-16610.html

原理直接拷贝CTFWIKI的原话

这个直接看图挺容易懂得,有点像数据结构的链表操作

这个就是unlink的基本操作,函数如下

#define unlink(P, BK, FD) { FD = P->fd; BK = P->bk; FD->bk = BK; BK->fd = FD; ... }

P就是当中的那块,BK就是最左边的那块,FD就是最右边的那块

一波血泪史,最早的时候unlink很简单,如下

一顿操作:

FD=P->fd = target addr -12BK=P->bk = expect valueFD->bk = BK,即 *(target addr-12+12)=BK=expect valueBK->fd = FD,即 *(expect value +8) = FD = target addr-12

这会有什么效果呢,加入我们的target addr放着某个got表项,那么当程序执行对应的函数时,就会直接执行我们需要的expect value

这里给个例子

/* Heap overflow vulnerable program. */ #include <stdlib.h> #include <string.h> int main( int argc, char * argv[] ) { char * first, * second; /*[1]*/ first = malloc( 666 ); /*[2]*/ second = malloc( 12 ); if(argc!=1) /*[3]*/ strcpy( first, argv[1] ); /*[4]*/ free( first ); /*[5]*/ free( second ); /*[6]*/ return( 0 ); }

通过strcpy可以覆盖second chunk的chunk header

假设被覆盖后的chunk header相关数据如下:

1) prev_size = 一个偶数,这样其PREV_INUSE 位就是0 了,即表示前一个chunk为free。 2) size = -4 3) fd = free 函数的got表地址address – 12

#实际的address的数据存放在数据段中(chunk非空闲时,fd\bk存放具体数据,所以指向的地址其实是free_addr-12) 4) bk = shellcode的地址

那么当程序在[4]处调用free(first)后会发生什么呢?

Unlink是把free掉的chunk从所属的bins链中,卸下来的操作(当然还包括一系列的检测机制),它是在free掉一块chunk(除fastbin大小的chunk外)之后,glibc检查这块chunk相邻的上下两块chunk的free状态之后,做出的向后合并或者向前合并引起的。

这里显然不可能是后向合并,first是第一块,能否执行前向合并,就要看下下块的PREV_INUSE字段是否是0了,此时nextsize被我们设置为了-4,这样glibc malloc就会将next chunk的prev_size字段看做是next-next chunk的size字段,而我们已经将next chunk的prev_size字段设置为了一个偶数,因此此时下下个chunk的PREV_INUSE字段为0,即下个chunk为free!开始执行向前合并

而unlink后就会把free函数的实际数据改变,等执行free(second)时,就会执行我们的shellcode了

一切看上去都是这么的美好

但是现在多了个检查机制,怎么检查的呢

// fd bk if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) malloc_printerr (check_action, "corrupted double-linked list", P, AV);

emmmm,肯定不满足呀,需要修改

接下来开始绕过:

首先我们通过覆盖,将 nextchunk 的 FD 指针指向了 fakeFD,将 nextchunk 的 BK 指针指向了 fakeBK 。那么为了通过验证,我们需要

fakeFD -> bk == P <=> *(fakeFD + 12) == PfakeBK -> fd == P <=> *(fakeBK + 8) == P

然后就可以执行unlink了

fakeFD -> bk = fakeBK <=> *(fakeFD + 12) = fakeBKfakeBK -> fd = fakeFD <=> *(fakeBK + 8) = fakeFD

那就意味着p->bk=nextchunk addr-8;p->fd=nextchunk addr-12,显然如果没有这个检测的话,我们可以构建任意的内容,但有了这个检测以后约束很大,但勉强可以改变chunk的指针

 

 

 

 

 

 

 

 

转载请注明原文地址: https://www.6miu.com/read-4932804.html

最新回复(0)