首页
社区
课程
招聘
[原创]【2019看雪CTF】Q2赛季 第五题 丛林的秘密 WP
2019-7-2 00:13 5452

[原创]【2019看雪CTF】Q2赛季 第五题 丛林的秘密 WP

2019-7-2 00:13
5452

【2019看雪CTF】Q2赛季 第五题 丛林的秘密 WP

安卓题,主要java层代码如下:

    static {
        System.loadLibrary("gogogo");
    }

    public MainActivity() {
        super();
        this.u = gogogoJNI.sayHello();
    }

    static EditText access$000(MainActivity arg0) {
        return arg0.eText1;
    }

    static TextView access$100(MainActivity arg0) {
        return arg0.txView1;
    }

    protected void onCreate(Bundle arg3) {
        super.onCreate(arg3);
        this.setContentView(2131296284);
        this.eText1 = this.findViewById(2131165238);
        this.txView1 = this.findViewById(2131165322);
        this.findViewById(2131165318).loadUrl(this.u);
        this.findViewById(2131165318).getSettings().setJavaScriptEnabled(true);
        this.button1 = this.findViewById(2131165218);
        this.button1.setOnClickListener(new View$OnClickListener() {
            public void onClick(View arg2) {
                if(gogogoJNI.check_key(MainActivity.this.eText1.getText().toString()) == 1) {
                    MainActivity.this.txView1.setText("Congratulations!");
                }
                else {
                    MainActivity.this.txView1.setText("Not Correct!");
                }
            }
        });
    }

有native代码,java层调用了其两个函数sayHellocheck_key
sayHello函数返回字串'http://127.0.0.1:8000' ,而check_key似乎不像校验函数:

signed int __cdecl Java_com_example_assemgogogo_gogogoJNI_check_key(JNIEnv *a1, int a2, int a3)
{
  const char *v3; // eax
  const char *v4; // esi
  const char *v5; // ebp
  unsigned int v6; // edi

  v3 = (*a1)->GetStringUTFChars(a1, (jstring)a3, 0);
  if ( v3 )
  {
    v4 = v3;
    ((void (__cdecl *)(JNIEnv *))(*a1)->ReleaseStringUTFChars)(a1);
    srand(0x32u);
    v5 = "d584a68d4e213d88w511v48e61g8d6e8";
    v6 = 0;
    while ( v4[v6] == (rand() % 128 != *v5) )
    {
      ++v6;
      ++v5;
      if ( v6 > 0x1F )
      {
        close(sock_fd_g);
        return 1;
      }
    }
  }
  return 0;
}

再看java层中的loadUrlsetJavaScriptEnabled,似乎有猫腻。再细看native代码。
JNI_OnLoad函数中调用了地址为B30的函数,解码了大量数据,并建立了socket,监听8000端口,模拟web服务,有客户端连接后将解码的数据发送到客户端。

 

数据解码后为html内容,内嵌WebAssembly,html部分内容为(去除了WebAssembly部分):

<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <meta charset="utf-8">
  <style>
    body {
      background-color: rgb(255, 255, 255);
    }
  </style>
</head>
<script>
...
...
...
function check_flag(){
  var value = document.getElementById("key_value").value;
  if(value.length != 32)
  {
  document.getElementById("tips").innerHTML = "Not Correct!";
  return;
  }
  instance.exports.set_input_flag_len(value.length);
  for(var ii=0;ii<value.length;ii++){
      instance.exports.set_input_flag(value[ii].charCodeAt(),ii);
  }
  var ret =  instance.exports.check_key();

  if (ret == 1){
   document.getElementById("tips").innerHTML = "Congratulations!"
  }
  else{
    document.getElementById("tips").innerHTML = "Not Correct!"
  }
}
</script>
<body>
   <div>Key: <input id="key_value" type="text" name="key" style="width:60%" ;="" value=""> <input type="submit" value="check" onclick="check_flag()"></div>
   <div> <label id="tips"></label></div>

</body></html

原来这才是真正的校验交互接口,校验逻辑自然在WebAssembly里。
将WebAssembly写入到文件,转成c再编译,最后反编,得到check_flag的伪代码如下:

__int64 check_key()
{
  unsigned int v0; // ST00_4

  if ( ++wasm_rt_call_stack_depth > 0x1F4u )
    wasm_rt_trap(7LL);
  o(1024u, 1025u, 1026u, 1027u);
  oo(1028u, 1029u, 1030u, 1031u);
  ooo(1032u, 1033u, 1034u, 1035u);
  oooo(1036u, 1037u, 1038u, 1039u);
  ooooo(0x410u, 0x411u, 0x412u, 0x413u);
  oooooo(0x414u, 0x415u, 0x416u, 0x417u);
  ooooooo(0x418u, 0x419u, 0x41Au, 0x41Bu);
  oooooooo(0x41Cu, 0x41Du, 0x41Eu, 0x41Fu);
  v0 = xxx();
  --wasm_rt_call_stack_depth;
  return v0;
}

先调用了8个函数对输入的32字节进行异或处理,然后调用xxx函数进行最终校验,xxx函数中通过32个32元一次代数方程进行校验,直接解方程得到

 y = [83,48,109,51,116,105,109,101,
      95,108,49,116,116,49,101,95,
      99,48,100,101,95,49,115,95,
      117,115,51,102,117,108,51,51]

即:S0m3time_l1tt1e_c0de_1s_us3ful33
32字节异或的常量值为:

x = [0x18, 0x09, 0x03, 0x6b, 0x01, 0x5a, 0x32, 0x57,
     0x30, 0x5d, 0x40, 0x46, 0x2b, 0x46, 0x56, 0x3d,
     0x02, 0x43, 0x17, 0x00, 0x32, 0x53, 0x1f, 0x26,
     0x2a, 0x01, 0x00, 0x10, 0x10, 0x1e, 0x40, 0x00]

直接异或得到原始输入:

>>> for i in range(32):
...   l.append(x[i]^y[i])
...
>>> print ''.join(map(chr,l))
K9nXu3_2o1q2_w3bassembly_r3vers3

[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回