Linux堆溢出漏洞利用之unlink(2)
时间:2018-08-02 09:46 来源:网络整理 作者:墨客科技 点击:次
整个操作与”向后合并“操作类似,再通过上述代码结合注释应该很容易理解free chunk的向前结合操作。在本例中当前chunk为first,它的下一个chunk为second,再下一个chunk为top chunk,此时 top chunk的 PREV_INUSE位是设置为1的(表示top chunk的前一个chunk,即second chunk, 已经使用),因此first的下一个chunk不会被“向前合并“掉。 介绍完向前、向后合并操作,下面就需要了解合并后(或因为不满足合并条件而没合并)的chunk该如何进一步处理了。在glibc malloc中,会将合并后的chunk放到unsorted bin中(还记得unsorted bin的含义么?)。相关代码如下: #!c /* Place the chunk in unsorted chunk list. Chunks are not placed into regular bins until after they have been given one chance to be used in malloc. */ bck = unsorted_chunks(av); //获取unsorted bin的第一个chunk /* /* The otherwise unindexable 1-bin is used to hold unsorted chunks. */ #define unsorted_chunks(M) (bin_at (M, 1)) */ fwd = bck->fd; …… p->fd = fwd; p->bk = bck; if (!in_smallbin_range(size)) { p->fd_nextsize = NULL; p->bk_nextsize = NULL; } bck->fd = p; fwd->bk = p; set_head(p, size | PREV_INUSE);//设置当前chunk的size,并将前一个chunk标记为已使用 set_foot(p, size);//将后一个chunk的prev_size设置为当前chunk的size /* /* Set size/use field */ #define set_head(p, s) ((p)->size = (s)) /* Set size at footer (only when chunk is not in use) */ #define set_foot(p, s) (((mchunkptr) ((char *) (p) + (s)))->prev_size = (s)) */ 上述代码完成的整个过程简要概括如下:将当前chunk插入到unsorted bin的第一个chunk(第一个chunk是链表的头结点,为空)与第二个chunk之间(真正意义上的第一个可用chunk);然后通过设置自己的size字段将前一个chunk标记为已使用;再更改后一个chunk的prev_size字段,将其设置为当前chunk的size。 注意:上一段中描述的”前一个“与”后一个“chunk,是指的由chunk的prev_size与size字段隐式连接的chunk,即它们在内存中是连续、相邻的!而不是通过chunk中的fd与bk字段组成的bin(双向链表)中的前一个与后一个chunk,切记!。 在本例中,只是将first chunk添加到unsorted bin中。 2.2 开始攻击现在我们再来分析如果一个攻击者在代码3中精心构造输入数据并通过strcpy覆盖了second chunk的chunk header后会发生什么情况。 假设被覆盖后的chunk header相关数据如下: 1) prev_size = 一个偶数,这样其PREV_INUSE 位就是0 了,即表示前一个chunk为free。 那么当程序在[4]处调用free(first)后会发生什么呢?我们一步一步分析。 一、向后合并 鉴于first的前一个chunk非free的,所以不会发生向后合并操作。 二、向前合并 先判断后一个chunk是否为free,前文已经介绍过,glibc malloc通过如下代码判断: #!c nextinuse = inuse_bit_at_offset(nextchunk, nextsize); 这里inuse_bit_at_offset宏定义如下: /* check/set/clear inuse bits in known places */ #define inuse_bit_at_offset(p, s) \ (((mchunkptr) (((char *) (p)) + (s)))->size & PREV_INUSE) PS:在本例中next chunk即second chunk,为了便于理解后文统一用next chunk。 从上面代码可以知道,它是通过将nextchunk + nextsize计算得到指向下下一个chunk的指针,然后判断下下个chunk的size的PREV_INUSE标记位。在本例中,此时nextsize被我们设置为了-4,这样glibc malloc就会将next chunk的prev_size字段看做是next-next chunk的size字段,而我们已经将next chunk的prev_size字段设置为了一个偶数,因此此时通过inuse_bit_at_offset宏获取到的nextinuse为0,即next chunk为free!既然next chunk为free,那么就需要进行向前合并,所以就会调用unlink(nextchunk, bck, fwd);函数。真正的重点就是这个unlink函数! 在前文2.1节中已经介绍过unlink函数的实现,这里为了便于说明攻击思路和过程,再详细分析一遍,unlink代码如下: #!c #define unlink(P, BK, FD) { \ FD = P->fd; \ BK = P->bk; \ FD->bk = BK; \ BK->fd = FD; \ ... } 此时P = nextchunk, BK = bck, FD = fwd。 1)首先FD = nextchunk->fd = free地址 – 12; 前面两步还好理解,主要是后面2步比较迷惑。我们作图理解: |