GKCTF 2021 Re复现

翻之前的记录发现GKCTF没有复现,水一篇

QQQQT

Enigma Virtual Box打包的QT程序,可以不脱壳
base58加密,字符串56fkoP8KhwCf3v7CEz

flag{12t4tww3r5e77}

Crash

观察字符串发现为go语言,7.5可以装一个插件,7.6可以直接反编译出。
在main_main函数中发现主要逻辑

if ( flag[1] == 43 && *(_DWORD *)v0 == 'TCKG' && *(_WORD *)(v0 + 4) == '{F' && *(_BYTE *)(v0 + 42) == '}' )
  {
    main_check(*flag, 43uLL);
    if ( v1 )
    {
      v6[0] = &qword_523A20;
      v6[1] = &off_5724B0;                      // Congratulation
      fmt_Fprintln((__int64)&off_574420, qword_625B28, (__int64)v6, 1LL);
    }
    

在main_check查看

第一次加密

  main_encrypto(a1 + 6, 24LL);                  // 3des
  if ( v3 == 44 )
  {
    runtime_memequal(v2, (__int64)"o/aWPjNNxMPZDnJlNp0zK5+NLPC4Tv6kqdJqjkL0XkA=");

在main_encrypto中

__int64 __usercall crypto_des_NewTripleDESCipher@<rax>(__int64 a1, __int64 a2)
{
  __int64 v3; // [rsp+20h] [rbp-10h]

  if ( a2 != 24 )
    return runtime_convT64(a2);
  v3 = runtime_newobject((__int64)&unk_534820);
  crypto_des___ptr_desCipher__generateSubkeys(v3, a1, 8LL);
  crypto_des___ptr_desCipher__generateSubkeys(v3 + 128, a1 + 8, 8LL);
  crypto_des___ptr_desCipher__generateSubkeys(v3 + 256, a1 + 16, 8LL);
  return v3;
}

可知为3des加密,并且可以得到key和iv

aKeyWelcometoth db '{',0Dh,0Ah          ; DATA XREF: .data:off_61E540↓o
.noptrdata:0000000000608840                 db '    "key": "WelcomeToTheGKCTF2021XXX",',0Dh,0Ah
.noptrdata:0000000000608840                 db '    "iv": "1Ssecret"',0Dh,0Ah
.noptrdata:0000000000608840                 db '}',0

第二次加密

    Encrypt_HashHex2(44LL);
    if ( v4 == 64 )
    {
      runtime_memequal(44LL, (__int64)"6e2b55c78937d63490b4b26ab3ac3cb54df4c5ca7d60012c13d2d1234a732b74");

进入加密函数

__int64 __usercall Encrypt_HashHex2@<rax>(__int64 a1, __int64 a2, __int64 a3)
{
  __int128 v4; // [rsp+18h] [rbp-50h]
  __int64 v5; // [rsp+18h] [rbp-50h]
  __int64 v6; // [rsp+20h] [rbp-48h]
  __int64 v7; // [rsp+48h] [rbp-20h]
  __int64 v8; // [rsp+50h] [rbp-18h]
  __int64 v9; // [rsp+58h] [rbp-10h]

  v8 = Encrypt_Sha256(a1, a2, a3);
  v7 = 2 * v6;
  v9 = runtime_makeslice((__int64)&unk_523B60, 2 * v6, 2 * v6);
  *((_QWORD *)&v4 + 1) = encoding_hex_Encode(v9, v7, v7, v8);
  runtime_slicebytetostring(0LL, v9, v7, v4);
  return v5;
}

加密为sha256

第三段加密

Encrypt_HashHex5(v7, v13, v16, v7, v13);
        if ( v14 == 128 )
        {
          runtime_memequal(
            v8,
            (__int64)"6500fe72abcab63d87f213d2218b0ee086a1828188439ca485a1a40968fd272865d5ca4d5ef5a651270a52ff952d955c9b7"
                     "57caae1ecce804582ae78f87fa3c9");

加密函数

__int64 __usercall Encrypt_HashHex5@<rax>(__int64 a1, __int64 a2, __int64 a3)
{
  __int128 v4; // [rsp+18h] [rbp-50h]
  __int64 v5; // [rsp+18h] [rbp-50h]
  __int64 v6; // [rsp+20h] [rbp-48h]
  __int64 v7; // [rsp+48h] [rbp-20h]
  __int64 v8; // [rsp+50h] [rbp-18h]
  __int64 v9; // [rsp+58h] [rbp-10h]

  v8 = Encrypt_Sha512(a1, a2, a3);
  v7 = 2 * v6;
  v9 = runtime_makeslice((__int64)&unk_523B60, 2 * v6, 2 * v6);
  *((_QWORD *)&v4 + 1) = encoding_hex_Encode(v9, v7, v7, v8);
  runtime_slicebytetostring(0LL, v9, v7, v4);
  return v5;
}

第四次加密

main_hash(a1 + 38, 4LL, 128LL, v8);
            if ( v9 == 32 )
              runtime_memequal(v3, (__int64)"ff6e2fd78aca4736037258f0ede4ecf0"

加密函数

__int64 __usercall main_hash@<rax>(__int64 a1, __int64 a2)
{
  const char *v2; // rdi
  _BYTE v4[24]; // [rsp+10h] [rbp-80h]
  __int64 v5; // [rsp+10h] [rbp-80h]
  __int64 v6; // [rsp+18h] [rbp-78h]
  __int128 v7; // [rsp+18h] [rbp-78h]
  __int64 v8; // [rsp+20h] [rbp-70h]
  __int64 v9; // [rsp+28h] [rbp-68h]
  __int64 v10; // [rsp+28h] [rbp-68h]
  __int128 v11; // [rsp+48h] [rbp-48h] BYREF
  char v12[32]; // [rsp+58h] [rbp-38h] BYREF
  __int128 v13; // [rsp+78h] [rbp-18h]

  v9 = runtime_stringtoslicebyte((__int64)v12, a1, a2, v6, v8);
  v10 = crypto_md5_Sum(v7, *((__int64 *)&v7 + 1), v9, v7);
  v11 = *(_OWORD *)&v4[8];
  v13 = 0LL;
  runtime_convT2Enoptr((__int64)&unk_524360, (__int64)&v11, v5, *(__int64 *)&v4[8]);
  v13 = *(_OWORD *)v4;
  fmt_Sprintf(v2);
  return v10;
}

md5

脚本

上述四种加密只有3DES为公钥加密可以解密,sha256,512,md5为hash加密需要爆破
itertools库爆破得更快

from Crypto.Cipher import DES3
import base64
import itertools
import string
import hashlib


def des3_cbc_decrypt(secret_key, secret_value, iv):
    def unpad(s): return s[0:-ord(s[-1])]
    res = DES3.new(secret_key.encode("utf-8"), DES3.MODE_CBC, iv)
    base64_decrypted = base64.b64decode(secret_value.encode("utf-8"))
    encrypt_text = res.decrypt(base64_decrypted)
    result = unpad(encrypt_text.decode())
    return result


def sha256crash(sha256enc):
    code = ''
    strlist = itertools.product(string.ascii_letters + string.digits, repeat=4)
    for i in strlist:
        code = i[0] + i[1] + i[2] + i[3]
        encinfo = hashlib.sha256(code.encode()).hexdigest()
        if encinfo == sha256enc:
            return code


def sha512crash(sha256enc):
    code = ''
    strlist = itertools.product(string.ascii_letters + string.digits, repeat=4)
    for i in strlist:
        code = i[0] + i[1] + i[2] + i[3]
        encinfo = hashlib.sha512(code.encode()).hexdigest()
        if encinfo == sha256enc:
            return code


def md5crash(sha256enc):
    code = ''
    strlist = itertools.product(string.ascii_letters + string.digits, repeat=4)

    for i in strlist:
        code = i[0] + i[1] + i[2] + i[3]
        encinfo = hashlib.md5(code.encode()).hexdigest()
        if encinfo == sha256enc:
            return code


if __name__ == '__main__':
    key = "WelcomeToTheGKCTF2021XXX"
    iv = b"1Ssecret"
    cipher = "o/aWPjNNxMPZDnJlNp0zK5+NLPC4Tv6kqdJqjkL0XkA="

    part1 = des3_cbc_decrypt(key, cipher, iv)
    part2 = sha256crash(
        "6e2b55c78937d63490b4b26ab3ac3cb54df4c5ca7d60012c13d2d1234a732b74")
    part3 = sha512crash(
        "6500fe72abcab63d87f213d2218b0ee086a1828188439ca485a1a40968fd272865d5ca4d5ef5a651270a52ff952d955c9b757caae1ecce804582ae78f87fa3c9")
    part4 = md5crash("ff6e2fd78aca4736037258f0ede4ecf0")

    flag = "GKCTF{" + part1 + part2 + part3 + part4 + "}"
    print(flag)
# GKCTF{87f645e9-b628-412f-9d7a-e402f20af940}

app-debug1

    static {
        System.loadLibrary("native-lib");
    }

    public native boolean check(String arg1) {
    }

    @Override  // androidx.appcompat.app.AppCompatActivity
    protected void onCreate(Bundle arg3) {
        super.onCreate(arg3);
        this.setContentView(0x7F09001C);  // layout:activity_main
        this.mBtnLogin = (Button)this.findViewById(0x7F070023);  // id:btn_login
        this.mEtflag = (EditText)this.findViewById(0x7F07001E);  // id:bEn_edittext
        this.mBtnLogin.setOnClickListener(new View.OnClickListener() {
            @Override  // android.view.View$OnClickListener
            public void onClick(View arg4) {
                if(MainActivity.this.check(MainActivity.this.mEtflag.getText().toString())) {
                    Toast.makeText(MainActivity.this, "You Are Right!flag is flag{md5(input)}", 0).show();
                    return;
                }

                Toast.makeText(MainActivity.this, "Sorry your flag is wrong!", 0).show();
            }
        });
    }
}

发现check函数,主要验证逻辑在native层
用压缩包打开lib文件夹里的so文件,IDA反编译

__int64 __fastcall Java_com_example_myapplication_MainActivity_check(__int64 a1, __int64 a2, __int64 a3)
{
  int i; // [xsp+24h] [xbp-4Ch]
  unsigned __int8 v5; // [xsp+4Ch] [xbp-24h]
  char v6[24]; // [xsp+50h] [xbp-20h] BYREF
  __int64 v7; // [xsp+68h] [xbp-8h]

  v7 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  sub_3FE20(a1, a3);
  if ( sub_40040(v6) == 7 )
  {
    for ( i = 0; i <= 6; ++i )
      byte_C80E0[i] = *(_BYTE *)sub_40064(v6, i);
    v5 = sub_3ED8C(byte_C80E0);
  }
  else
  {
    v5 = 0;
  }
  sub_3FC04(v6);
  _ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
  return v5;
}

加密函数,很明显是tea加密

bool __fastcall sub_3ED8C(unsigned int *a1)
{
  unsigned int i; // [xsp+20h] [xbp-20h]
  int sum; // [xsp+24h] [xbp-1Ch]
  unsigned int v1; // [xsp+28h] [xbp-18h]
  unsigned int v0; // [xsp+2Ch] [xbp-14h]

  v0 = *a1;
  v1 = a1[1];
  sum = 0;
  for ( i = 0; i < 0x20; ++i )
  {
    sum += dword_C8010;
    v0 += (16 * v1 + dword_C8000) ^ (v1 + sum) ^ ((v1 >> 5) + dword_C8004);
    v1 += (16 * v0 + dword_C8008) ^ (v0 + sum) ^ ((v0 >> 5) + dword_C800C);
  }
  *a1 = v0;
  a1[1] = v1;
  return *a1 == 0xF5A98FF3 && a1[1] == 0xA21873A3;
}

利用TracerPid的反调试,对付这种反调试是把TracerPid的值改为0就行了,这个反调试实际上就是将check函数里面的key给改了,当发现调试器时,会使用假的key,只要没有检测到调试器才会使用真key

修改key的值的函数

void sub_3EF18()
{
  dword_C8004 = 7;
  dword_C8008 = 8;
  dword_C800C = 6;
}

脚本

就是tea加密,注意key在调用时发生变化

#include <stdio.h>
#include <stdint.h>
//加密函数
void encrypt(uint32_t *v, uint32_t *k)
{
    uint32_t v0 = v[0], v1 = v[1], sum = 0, i;           /* set up */
    uint32_t delta = 0x9e3779b9;                         /* a key schedule constant */
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
    for (i = 0; i < 32; i++)
    { /* basic cycle start */
        sum += delta;
        v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
    } /* end cycle */
    v[0] = v0;
    v[1] = v1;
}
//解密函数
void decrypt(uint32_t *v, uint32_t *k)
{
    uint32_t v0 = v[0], v1 = v[1], i; /* set up */
    uint32_t delta = 0x458BCD42;
    uint32_t sum = delta * 32;                           /* a key schedule constant */
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3]; /* cache key */
    for (i = 0; i < 32; i++)
    { /* basic cycle start */
        v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
        v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        sum -= delta;
    } /* end cycle */
    v[0] = v0;
    v[1] = v1;
}

int main()
{
    uint32_t v[32] = {0xF5A98FF3, 0xA21873A3}, k[4] = {9, 7, 8, 6};
    // v为要加密的数据是两个32位无符号整数
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
    // printf("string: %u %u\n", v[0], v[1]);
    // encrypt(v, k);
    // printf("encode: %x %x\n", v[0], v[1]);
    decrypt(v, k);
    printf("decode: %x %x\n", v[0], v[1]);
    unsigned long len = 8;
    for (int i = 0; i < len; i++)
    {
        printf("%c", (*((char *)v + i)) & 0xff);
    }
    return 0;
}

得出GKcTFg0注意需要md5加密
flag{77bca47fe645ca1bd1ac93733171c9c4}

SoMuchCode

找到scanf
在这里插入图片描述
交叉引用找到调用他的函数,sub_140001310(8i64, flag, &dword_14000A6F8);
很明显是xxtea加密
在这里插入图片描述
发现就是一个xxtea加密,改了dealt,中间的for循环没了,变成好多步,导致一堆代码。然后后面就需要得到密文了,密文是在后面调试过程中生成的。直接用IDA调,得出数据

脚本

#include <stdio.h>
#include <stdlib.h>
#define DELTA 0x33445566
int main()
{
    unsigned int v[8] = {0x993CAB5C, 0x3F40E129, 0x777791DE, 0x737DFEA6, 0x0ECCF59E6, 0x0C9604CE3, 0x9682C0A5, 0x556F2A1E};
    unsigned int key[4] = {0x000036B0, 0x00013816, 0x00000010, 0x0001E0F3};
    unsigned int sum = 0;
    unsigned int y,z,p,rounds,e;
    int n = 8;
    int i = 0;
    rounds = 12;
    y = v[0];
    sum = (rounds*DELTA)&0xffffffff;
     do                 //0x9E3779B9*(52/35)-0x4AB325AA,测试来要循环7次
     {
        e = sum >> 2 & 3;
        for(p=n-1;p>0;p--)    //34次循环
        {
            z = v[p-1];
            v[p] = (v[p] - ((((z>>5)^(y<<2))+((y>>3)^(z<<4))) ^ ((key[(p^e)&3]^z)+(y ^ sum)))) & 0xffffffff;
            y = v[p];
        }
        z = v[n-1];
        v[0] = (v[0] - (((key[(p^e)&3]^z)+(y ^ sum)) ^ (((y<<2)^(z>>5))+((z<<4)^(y>>3))))) & 0xffffffff;
        y = v[0];
        sum = (sum-DELTA)&0xffffffff;
     }while(--rounds);
    for(i=0;i<8;i++)
    {
        printf("%c%c%c%c",*((char*)&v[i]+0),*((char*)&v[i]+1),*((char*)&v[i]+2),*((char*)&v[i]+3));
    }
    return 0;
}

flag{9b34a61df773acf0e4dec25ea5fb0e29}

KillerAid -未解出

看官方WP是AES加密,我死活看不出来,就先这样吧,这破题看一晚上之后补上

一共两个文件 exe 是用C#开发的WinForm NETFrameWork
使用ILSpy反编译exe文件看到在MainWindows中的逻辑

private void Check_Click(object sender, RoutedEventArgs e)
	{
		if (string.IsNullOrEmpty(ID.Text) || string.IsNullOrEmpty(Code.Text))
		{
			MessageBox.Show("ID 或 Code不能为空!");
			return;
		}
		byte[] bytes = Encoding.ASCII.GetBytes(ID.Text);
		byte[] bytes2 = Encoding.ASCII.GetBytes(Code.Text);
		int num = Math.Max(bytes.Length, bytes2.Length);
		byte[] array = new byte[9] { 7, 90, 115, 1, 117, 99, 114, 97, 24 };
		for (int i = 0; i < num; i++)
		{
			bytes[i % bytes.Length] ^= bytes2[i % bytes2.Length];
		}
		for (int j = 0; j < bytes.Length; j++)
		{
			if (bytes[j] != array[j])
			{
				MessageBox.Show("请检查ID 或 Code 是否正确");
				return;
			}
		}
		if (!CheckCode(Code.Text))
		{
			MessageBox.Show("请检查ID 或 Code 是否正确");
			return;
		}
		MessageBox.Show("flag{" + ID.Text + "@" + Code.Text + "}");
	}

DLL文件

应该不可能爆破吧,看看DLL文件
好难看,为什么有那么多do-while
行移位

	v12 = v2[1];
    v2[1] = v2[5];
    v2[5] = v2[9];
    v2[9] = v2[13];
    v13 = v2[10];
    v2[13] = v12;
    v14 = v2[2];
    v2[2] = v13;
    v15 = v2[14];
    v2[10] = v14;
    v16 = v2[6];
    v2[6] = v15;
    v17 = v2[15];
    v2[14] = v16;
    v18 = v2[3];
    v2[3] = v17;
    v2[15] = v2[11];
    v2[11] = v2[7];
    v2[7] = v18;

列位移

do
    {
      v2_0 = *(v19 - 2);
      v2_1 = *(v19 - 1);
      v2_3 = v19[1];
      v2_2 = *v19;
      v19 += 4;
      v25 = v2_1 ^ v2_0 ^ v2_2 ^ v2_3;
      *(v19 - 6) = v25 ^ v2_0 ^ (2 * (v2_1 ^ v2_0)) ^ (27 * ((unsigned __int8)(v2_1 ^ v2_0) >> 7));
      *(v19 - 5) = v25 ^ v2_1 ^ (2 * (v2_2 ^ v2_1)) ^ (27 * ((unsigned __int8)(v2_2 ^ v2_1) >> 7));
      *(v19 - 4) = v25 ^ v2_2 ^ (2 * (v2_2 ^ v2_3)) ^ (27 * ((unsigned __int8)(v2_2 ^ v2_3) >> 7));
      *(v19 - 3) = v25 ^ v2_3 ^ (2 * (v2_3 ^ v2_0)) ^ (27 * ((unsigned __int8)(v2_3 ^ v2_0) >> 7));
      --v20;
    }
    while ( v20 );

通过上述可得code:Meaningless_!$!%*@^%#%_Code

脚本

from z3 import *
flag2 = bytearray(b'Meaningless_!$!%*@^%#%_Code')
id = [BitVec('a%d' % i, 8) for i in range(9)]
fuckId = [i for i in id]
target =[7, 90, 115, 1, 117, 99, 114, 97, 24]
tt = bytearray(target)
for j in range(len(flag2)):
    id[j % len(target)] ^= flag2[j % len(flag2)]
s = Solver()
for i in range(9):
    s.add(id[i] == target[i])
s.check()
res = s.model()
id_ = ''
for i in range(9):
    id_ += chr(res[fuckId[i]].as_long())
print(id_)
# ginkgo_CX

flagflag{ginkgoCX@Meaningless!$!%*@^%#%_Code}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值