off-by-one漏洞的利用

本文详细介绍了off-by-one漏洞的原理和两种利用方法,包括堆块重叠导致的数据泄露与控制流程,以及通过main_arena获取libc基址并利用realloc调整栈针的技术细节,最终实现shell的获取。
摘要由CSDN通过智能技术生成

介绍

严格来说 off-by-one 漏洞是一种特殊的溢出漏洞,off-by-one 指程序向缓冲区中写入时,写入的字节数超过了这个缓冲区本身所申请的字节数并且只越界了一个字节。

漏洞原理

off-by-one 是指单字节缓冲区溢出,这种漏洞的产生往往与边界验证不严和字符串操作有关,当然也不排除写入的 size 正好就只多了一个字节的情况。其中边界验证不严通常包括

  • 使用循环语句向堆块中写入数据时,循环的次数设置错误(这在 C 语言初学者中很常见)导致多写入了一个字节。
  • 字符串操作不合适

一般来说,单字节溢出被认为是难以利用的,但是因为 Linux 的堆管理机制 ptmalloc 验证的松散性,基于 Linux 堆的 off-by-one 漏洞利用起来并不复杂,并且威力强大。 此外,需要说明的一点是 off-by-one 是可以基于各种缓冲区的,比如栈、bss 段等等,但是堆上(heap based) 的 off-by-one 是 CTF 中比较常见的。我们这里仅讨论堆上的 off-by-one 情况。

利用方法

1.溢出字节为可控制任意字节:通过修改大小造成块结构之间出现重叠,从而泄露其他块数据,或是覆盖其他块数据。也可使用 NULL 字节溢出的方法

2.溢出字节为 NULL 字节:在 size 为 0x100 的时候,溢出 NULL 字节可以使得 prev_in_use 位被清,这样前块会被认为是 free 块。(1) 这时可以选择使用 unlink 方法(见 unlink 部分)进行处理。(2) 另外,这时 prev_size 域就会启用,就可以伪造 prev_size ,从而造成块之间发生重叠。此方法的关键在于 unlink 的时候没有检查按照 prev_size 找到的块的大小与prev_size 是否一致。

我们现在先通过heapcreator这一题来看第一种利用情况。用ida查看分析伪代码,在主函数中可以清楚的看到程序提供的主要功能。

switch ( atoi(&buf) )
    {
   
      case 1:
        create_heap(&buf, &buf);
        break;
      case 2:
        edit_heap(&buf, &buf);
        break;
      case 3:
        show_heap(&buf, &buf);
        break;
      case 4:
        delete_heap(&buf, &buf);
        break;
      case 5:
        exit(0);
        return;
      default:
        v4 = "Invalid Choice";
        puts("Invalid Choice");
        break;
    }

我们对每一个功能逐步进行分析,create_heap功能

unsigned __int64 create_heap()
{
   
  _QWORD *v0; // rbx
  signed int i; // [rsp+4h] [rbp-2Ch]
  size_t size; // [rsp+8h] [rbp-28h]
  char buf; // [rsp+10h] [rbp-20h]
  unsigned __int64 v5; // [rsp+18h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  for ( i = 0; i <= 9; ++i )
  {
   
    if ( !heaparray[i] )
    {
   
      heaparray[i] = malloc(0x10uLL);
      if ( !heaparray[i] )
      {
   
        puts("Allocate Error");
        exit(1);
      }
      printf("Size of Heap : ");
      read(0, &buf, 8uLL);
      size = atoi(&buf);
      v0 = heaparray[i];
      v0[1] = malloc(size);
      if ( !*((_QWORD *)heaparray[i] + 1) )
      {
   
        puts("Allocate Error");
        exit(2);
      }
      *(_QWORD *)heaparray[i] = size;
      printf("Content of heap:", &buf);
      read_input(*((_QWORD *)heaparray[i] + 1), size);
      puts("SuccessFul");
      return __readfsqword(0x28u) ^ v5;
    }
  }
  return __readfsqword(0x28u) ^ v5;
}

根据以上代码可以分析出程序的存储结构,下图是create了0x20大小的heap的情况,其中heaparry数组存放了chunk1的mem指针,chunk1中存放了heap的size和存储实际内容的chunk2的mem指针,chunk2存放了实际的heap的内容。

image-20200713154932938

edit_heap功能根据chunk1中存放的指针对chunk2中的内容进行修改,要注意的是可写入的长度是size+1。

if ( heaparray[v1] )
  {
   
    printf("Content of heap : ", &buf);
    read_input(*((void **)heaparray[v1] + 1), *(_QWORD *)heaparray[v1] + 1LL);
    puts("Done !");
  }

show_heap功能根据chunk1中存放的指针对chunk2中的内容进行打印

if ( heaparray[v1] )
  {
   
    printf("Size : %ld\nContent : %s\n", *(_QWORD *)</
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值