CTF-RE dont_panic (pin二进制打桩暴力)

部分参考题解:http://eternal.red/2017/dont_panic-writeup/

方便起见,题目文件名改成go

运行题目程序,发现flag是跟在命令行参数之后的。用IDA打开后发现没有找到main函数,因此选择在Linux下查看段信息:

supergate@ubuntu:~/Desktop/task1$ readelf -S go

得到信息如下:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000401000  00001000
       000000000007ae40  0000000000000000  AX       0     0     16
  [ 2] .rodata           PROGBITS         000000000047c000  0007c000
       0000000000033f5b  0000000000000000   A       0     0     32
  [ 3] .typelink         PROGBITS         00000000004b0080  000b0080
       0000000000000b4c  0000000000000000   A       0     0     32
  [ 4] .itablink         PROGBITS         00000000004b0bd0  000b0bd0
       0000000000000038  0000000000000000   A       0     0     8
  [ 5] .gosymtab         PROGBITS         00000000004b0c08  000b0c08
       0000000000000000  0000000000000000   A       0     0     1
  [ 6] .gopclntab        PROGBITS         00000000004b0c20  000b0c20
       0000000000044d5d  0000000000000000   A       0     0     32
  [ 7] .noptrdata        PROGBITS         00000000004f6000  000f6000
       0000000000002608  0000000000000000  WA       0     0     32
  [ 8] .data             PROGBITS         00000000004f8620  000f8620
       0000000000001cf0  0000000000000000  WA       0     0     32
  [ 9] .bss              NOBITS           00000000004fa320  000fa310
       000000000001a908  0000000000000000  WA       0     0     32
  [10] .noptrbss         NOBITS           0000000000514c40  000fa310
       00000000000046a0  0000000000000000  WA       0     0     32
  [11] .note.go.buildid  NOTE             0000000000400fc8  00000fc8
       0000000000000038  0000000000000000   A       0     0     4
  [12] .shstrtab         STRTAB           0000000000000000  000fa310
       0000000000000073  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

 发现了.gosymtab等这种go语言特有的段名称,因此可以推断出这是一个用go语言写出的程序。

在输入错误的flag时会出现提示信息 Nope.。不知道为什么ida的strings找不到这个字符串。所以用linux下的xxd来找到这个字符串:

supergate@ubuntu:~/Desktop/task1$ xxd -g1 go | grep Nope.
000a5240: 3e 45 72 72 6e 6f 45 72 72 6f 72 4e 6f 70 65 2e  >ErrnoErrorNope.

找到了ErrnoErrorNope.所在的起始位置为0xa5240,经过计算知道Nope.的真正位置是0xa524b。我们再查找一下程序的什么位置调用了这个位置:

supergate@ubuntu:~/Desktop/task1$ objdump -d go | grep a524b
  47ba23:	48 8d 05 21 98 02 00 	lea    0x29821(%rip),%rax        # 0x4a524b

可知函数中0x47ba23处调用了这个字符串,因此我们在ida中定位这个位置,即可找到主要函数所在。 

主要函数的C语言伪代码如下所示:

__int64 __fastcall sub_47B8A0(void **a1, __int64 a2, unsigned __int8 *a3, __int64 a4, __int64 a5, __int64 a6)
{
  __int64 pswd; // rcx
  signed __int64 cur; // rax
  void **ptr; // rdx
  char K; // bl
  __int64 v10; // rdx
  __int64 v11; // rdx
  __int64 v12; // r8
  __int64 v13; // rdx
  __int64 v14; // r8
  __int64 v16; // rdx
  __int64 v17; // rdx
  __int64 v18; // rdx
  __int64 v19; // rcx
  __int64 v20; // rdx
  __int64 v21; // r8
  __int64 v22; // [rsp+10h] [rbp-F0h]
  char ENC; // [rsp+18h] [rbp-E8h]
  signed __int64 v24; // [rsp+20h] [rbp-E0h]
  __int64 v25; // [rsp+28h] [rbp-D8h]
  void **v26; // [rsp+30h] [rbp-D0h]
  char v27; // [rsp+46h] [rbp-BAh]
  char v28; // [rsp+47h] [rbp-B9h]
  void **v29; // [rsp+48h] [rbp-B8h]
  signed __int64 LEN; // [rsp+58h] [rbp-A8h]
  __int64 v31; // [rsp+60h] [rbp-A0h]
  __int64 v32; // [rsp+68h] [rbp-98h]
  __int64 v33; // [rsp+70h] [rbp-90h]
  void *v34; // [rsp+78h] [rbp-88h]
  __int64 v35; // [rsp+80h] [rbp-80h]
  void *v36; // [rsp+88h] [rbp-78h]
  __int64 v37; // [rsp+90h] [rbp-70h]
  void *v38; // [rsp+98h] [rbp-68h]
  __int64 v39; // [rsp+A0h] [rbp-60h]
  __int64 v40; // [rsp+A8h] [rbp-58h]
  __int64 v41; // [rsp+B0h] [rbp-50h]
  void *v42; // [rsp+B8h] [rbp-48h]
  __int64 v43; // [rsp+C0h] [rbp-40h]
  __int64 v44; // [rsp+C8h] [rbp-38h]
  __int64 v45; // [rsp+D0h] [rbp-30h]
  __int64 v46; // [rsp+D8h] [rbp-28h]
  __int64 v47; // [rsp+E0h] [rbp-20h]
  __int64 v48; // [rsp+E8h] [rbp-18h]
  __int64 v49; // [rsp+F0h] [rbp-10h]

  while ( (unsigned __int64)&v37 <= *(_QWORD *)(__readfsqword(0xFFFFFFF8) + 16) )
    sub_44AFC0((__int64)a1, a2);
  if ( parameters_cnter != 2 )
  {
    v36 = &unk_4A5363;
    v37 = 6LL;
    v34 = &unk_4A5199;
    v35 = 4LL;
    a1 = &v42;
    sub_44D5E0(&v42);
    sub_40B8F0((__int64)&v42, a2, v16, (__int64)&v36);
    v44 = v24;
    v45 = v25;
    if ( !parameters_cnter )
      sub_4248C0(&v42, a2, v17, qword_4FA5C0);
    sub_40B8F0((__int64)&v42, a2, v17, qword_4FA5C0);
    v46 = v24;
    v47 = v25;
    sub_40B8F0((__int64)&v42, a2, v18, (__int64)&v34);
    v19 = v24;
    v48 = v24;
    v49 = v25;
    ENC = 3;
    v24 = 3LL;
    sub_474860((__int64)&v42, a2, v20, v19, v21);
    v22 = 1LL;
    sub_45D8B0((__int64)&v42);
  }
  if ( (unsigned __int64)parameters_cnter <= 1 )
    sub_4248C0(a1, a2, a3, parameters_cnter);
  pswd = *(_QWORD *)(qword_4FA5C0 + 16);
  v31 = *(_QWORD *)(qword_4FA5C0 + 16);
  cur = *(_QWORD *)(qword_4FA5C0 + 24);
  LEN = cur;
  if ( cur >= 42 )
  {
    ptr = 0LL;
    K = 0;
    v29 = 0LL;
    v27 = 0;
    if ( cur <= 0 )
    {
LABEL_11:
      v42 = &unk_4A8374;
      v43 = 28LL;
      sub_40B8F0((__int64)a1, a2, (__int64)ptr, (__int64)&v42);
      v32 = v24;
      v33 = v25;
      v24 = 1LL;
      sub_474860((__int64)a1, a2, v11, v25, v12);
      sub_45D8B0((__int64)a1);
    }
    else
    {
      while ( 1 )
      {
        a2 = *((unsigned __int8 *)ptr + pswd);
        if ( (unsigned __int8)a2 >= 0x80u )
        {
          v22 = pswd;
          ENC = cur;
          v24 = (signed __int64)ptr;
          sub_447350((__int64)a1, a2, (__int64)ptr, pswd, a5, a6);
          a2 = (unsigned int)v25;
          a1 = v26;
          pswd = v31;
          ptr = v29;
          K = v27;
        }
        else
        {
          a2 = (unsigned __int8)a2;
          a1 = (void **)((char *)ptr + 1);
        }
        v28 = a2 + K;
        LOBYTE(v22) = a2 + K;
        sub_47B760((__int64)a1, a2, (__int64)ptr, pswd, a5, a6, v22, ENC);
        if ( (unsigned __int64)v29 >= 0x2A )
          sub_4248C0(a1, a2, v10, v29);
        a3 = byte_4F6260;
        pswd = byte_4F6260[(_QWORD)v29];
        if ( ENC != (_BYTE)pswd )
          break;
        LOBYTE(cur) = LEN;
        pswd = v31;
        ptr = a1;
        K += a2;
        v29 = a1;
        v27 = v28;
        if ( (signed __int64)a1 >= LEN )
          goto LABEL_11;
      }
    }
  }
  v38 = &Nope_str;
  v39 = 5LL;
  v40 = 0LL;
  v41 = 0LL;
  sub_40B8F0((__int64)a1, a2, (__int64)a3, pswd);
  v40 = v24;
  v41 = v25;
  sub_474860((__int64)a1, a2, v13, v25, v14);
  return sub_45D8B0((__int64)a1);
}

在命令行参数满足条件的情况下,主要流程是判断flag长度需要大于等于42,然后逐位进行加密,并且和常数数组进行比较。

知道流程后,可以想到逆向程序加密算法从而解决。也可以用pin打桩的方法来解决

打桩需要在linux下安装pin,这个教程比较多,就不再赘述。

对于本题的情况,首先我们知道对flag的检查是加密后和常数数组逐位比较的,并且比较的次数和当前比较的位数是有一定关系(i+2=cmp_cnt),特殊情况是flag正确时,比较全部完成(i+1=cmp_cnt=len)。因此我们只需将inscount0.cpp修改一下即可。

我们找到了“比较”这一操作位于0x047B96E处,因此只需在inscount0.cpp进行如下修改:

VOID docount() { icount++; }

改成:

VOID docount(void *ip) 
{
	if ((long long int)ip == 0x000000000047B96E)
	 icount++; 
}

 由于docount函数的参数改变,因此调用这个函数的函数内部也应该进行修改:

INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);

 改成:

INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_INST_PTR, IARG_END);

需要将这个文件保存后编译为.so文件:

[MyPintools]$ make obj-intel64/MyPinTool.so TARGET=intel64

 然后进行暴力打桩即可,脚本如下:(注意,以下脚本有个小问题导致最后一位跑不出来,不过比较容易发现)

import os

charset="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0 123456789_-+*{}'"
flag="hxp{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}"
File_loc="/home/supergate/Desktop/Pin/pin/pin -t /home/supergate/Desktop/Pin/pin/source/tools/MyPinTool/obj-intel64/MyPinTool.so -- /home/supergate/Desktop/task1/go \""

def check():
    os.system(File_loc+flag+"\"")
    return int(read("inscount.out").split(" ")[1])

def read(fname):
    with open(fname) as f:return f.read()

while(True):
     for i in range(4,40):
	for ch in charset:
	    tempflag=list(flag)
	    tempflag[i]=ch
	    flag=''.join(tempflag)
	    res=check()
	    print flag
	    if res==i+2:break
	    if res==42:
		print flag
		exit(0)

最终正确答案为:hxp{k3eP_C4lM_AnD_D0n't_P4n1c__G0_i5_S4F3}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值