[GKCTF 2021]Crash
首先查壳,64bit,elf文件
刚解压完发现只有一个文件,大小足足有2M,我感觉不太简单,ida64, 定位到main函数
main_main()关键部分
我看到fmt_Fprintln
这个的时候就反应过来了,这是道go的题。
头一次做go的题,没有基础,就试着一点一点分析了:(很多地方都参考了wp)
首先分析main函数关键部分
if ( flag[1] == 43 && *(_DWORD *)v0 == 'TCKG' && *(_WORD *)(v0 + 4) == '{F' && *(_BYTE *)(v0 + 42) == '}' )
{
main_check(*flag, 0x2BuLL);
if ( v1 )
{
v6[0] = &unk_523A20;
v6[1] = &off_5724B0;
fmt_Fprintln((__int64)&off_574420, qword_625B28, (__int64)v6, 1LL, 1LL);
}
满足if条件后执行main_check函数
main_check()
void __golang main_check(__int64 a1, unsigned __int64 a2)
{
__int64 v2; // [rsp+10h] [rbp-68h]
__int64 v3; // [rsp+10h] [rbp-68h]
__int64 v4; // [rsp+18h] [rbp-60h]
char v5; // [rsp+18h] [rbp-60h]
__int64 v6; // [rsp+18h] [rbp-60h]
char v7; // [rsp+18h] [rbp-60h]
__int64 v8; // [rsp+18h] [rbp-60h]
__int64 v9; // [rsp+20h] [rbp-58h]
__int64 v10; // [rsp+20h] [rbp-58h]
char v11[32]; // [rsp+30h] [rbp-48h] BYREF
char v12[32]; // [rsp+50h] [rbp-28h] BYREF
if ( a2 < 0x1E )
runtime_panicSliceAlen();
v2 = main_encrypto(a1 + 6, 24LL);
if ( v4 == 44 )
{
v9 = runtime_memequal(v2, (__int64)"o/aWPjNNxMPZDnJlNp0zK5+NLPC4Tv6kqdJqjkL0XkA=", 44LL, 44);
if ( v5 )
{
if ( a2 < 0x22 )
runtime_panicSliceAlen();
v6 = runtime_stringtoslicebyte((__int64)v11, a1 + 30, 4LL);
Encrypt_HashHex2(v6, v9);
if ( v9 == 64 )
{
v10 = runtime_memequal(
v6,
(__int64)"6e2b55c78937d63490b4b26ab3ac3cb54df4c5ca7d60012c13d2d1234a732b74",
64LL,
v6);
if ( v7 )
{
if ( a2 < 0x26 )
runtime_panicSliceAlen();
v8 = runtime_stringtoslicebyte((__int64)v12, a1 + 34, 4LL);
Encrypt_HashHex5(v8, v10);
if ( v10 == 128 )
{
runtime_memequal(
v8,
(__int64)"6500fe72abcab63d87f213d2218b0ee086a1828188439ca485a1a40968fd272865d5ca4d5ef5a651270a52ff952d955c9"
"b757caae1ecce804582ae78f87fa3c9",
128LL,
v8);
if ( (_BYTE)v8 )
{
if ( a2 < 0x2A )
runtime_panicSliceAlen();
main_hash(a1 + 38, 4LL);
if ( v8 == 32 )
runtime_memequal(v3, (__int64)"ff6e2fd78aca4736037258f0ede4ecf0", 32LL, 32);
}
}
}
}
}
}
}
里面的逻辑就是把flag分为四部分,然后分别进行了不同的加密
第一部分
v2 = main_encrypto(a1 + 6, 24LL);
if ( v4 == 44 )
{
v9 = runtime_memequal(v2, (__int64)"o/aWPjNNxMPZDnJlNp0zK5+NLPC4Tv6kqdJqjkL0XkA=", 44LL, 44);
main_encrypto()
__int64 __usercall main_encrypto@<rax>(__int64 a1, __int64 a2)
{
__int64 v3; // [rsp+18h] [rbp-100h]
__int64 v4; // [rsp+20h] [rbp-F8h]
__int64 v5; // [rsp+20h] [rbp-F8h]
__int64 v6; // [rsp+28h] [rbp-F0h]
__int64 v7; // [rsp+48h] [rbp-D0h]
__int64 v8; // [rsp+60h] [rbp-B8h]
__int64 v9; // [rsp+68h] [rbp-B0h]
__int64 v10; // [rsp+70h] [rbp-A8h]
__int64 v11; // [rsp+78h] [rbp-A0h]
char v12[32]; // [rsp+B0h] [rbp-68h] BYREF
__int64 v13; // [rsp+D0h] [rbp-48h]
__int64 v14; // [rsp+D8h] [rbp-40h]
__int64 v15; // [rsp+E0h] [rbp-38h]
__int64 v16; // [rsp+E8h] [rbp-30h]
__int64 v17; // [rsp+F0h] [rbp-28h]
__int64 *v18; // [rsp+F8h] [rbp-20h]
_QWORD v19[2]; // [rsp+100h] [rbp-18h] BYREF
v18 = (__int64 *)runtime_newobject((__int64)" ");
v6 = encoding_json_Unmarshal((__int64)off_61E540, qword_61E548, qword_61E550, (__int64)"\b", (__int64)v18);
v13 = *v18;
v10 = v18[1];
v14 = v18[2];
v11 = v18[3];
v15 = runtime_stringtoslicebyte(0LL, a1, a2);
v16 = runtime_stringtoslicebyte((__int64)v12, v13, v10);
v3 = runtime_stringtoslicebyte(0LL, v14, v11);
Encrypt_DesEncrypt(v15, v4, v6, v16, v4, v6, v3, v4);
v17 = v7;
if ( v8 )
{
v19[0] = *(_QWORD *)(v8 + 8);
v19[1] = v9;
fmt_Fprintln((__int64)&off_574420, qword_625B28, (__int64)v19, 1LL, 1LL);
}
encoding_base64___ptr_Encoding__EncodeToString(qword_625AD0, v17);
return v5;
}
在其中代码里找到了Encrypt_DesEncrypt()函数,由于是以encrypt命名的,所以猜测代码的加密在这个函数里,继续查看该函数内部,该函数内部第一个crypto_des_NewTripleDESCipher函数引起了注意,其内部是这样的
__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;
}
去查看了一下wp,发现这是3des加密算法,然后搜集了一些3des加密算法的信息,要解密的话需要得到密钥,一般的题目好像都会有welcome这样的话,我就直接搜素了一下,然后直接找到了key 和 iv,然后要加密的字符串是"o/aWPjNNxMPZDnJlNp0zK5+NLPC4Tv6kqdJqjkL0XkA="
然后就模仿wp放在在线解密工具中,得到了第一部分
87f645e9-b628-412f-9d7a-
第二部分加密
Encrypt_HashHex2(44LL, v4);
if ( v4 == 64 )
{
runtime_memequal(44LL,(__int64)"6e2b55c78937d63490b4b26ab3ac3cb54df4c5ca7d60012c13d2d1234a732b74");
}
Encrypt_HashHex2()
__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加密
直接上python爆破
import hashlib
hash="6e2b55c78937d63490b4b26ab3ac3cb54df4c5ca7d60012c13d2d1234a732b74"
for i in range(90000000):
pwd1=hex(i)
pwd1=pwd1[2:]
h1=hashlib.sha256(pwd1.encode('utf-8')).hexdigest()
#print(h1)
if h1==hash:
print(hex(i))
break
#0xe402
第三部分加密
Encrypt_HashHex5(v8, v10);
if ( v10 == 128 )
{
runtime_memequal(v8,(__int64)"6500fe72abcab63d87f213d2218b0ee086a1828188439ca485a1a40968fd272865d5ca4d5ef5a651270a52ff952d955c9b757caae1ecce804582ae78f87fa3c9", 128LL,v8);
Encrypt_HashHex5()
__int64 __usercall Encrypt_HashHex5@<rax>(__int64 a1, __int64 a2, __int64 a3)
{
__int64 v4; // [rsp+18h] [rbp-50h]
__int128 v5; // [rsp+18h] [rbp-50h]
__int64 v6; // [rsp+18h] [rbp-50h]
__int64 v7; // [rsp+20h] [rbp-48h]
__int64 v8; // [rsp+48h] [rbp-20h]
__int64 v9; // [rsp+50h] [rbp-18h]
__int64 v10; // [rsp+58h] [rbp-10h]
v9 = Encrypt_Sha512(a1, a2, a3);
v8 = 2 * v7;
runtime_makeslice((__int64)&unk_523B60, 2 * v7, 2 * v7, v9);
v10 = v4;
*((_QWORD *)&v5 + 1) = encoding_hex_Encode(v4, v8, v8, v9);
runtime_slicebytetostring(0LL, v10, v8, v5);
return v6;
}
可知是sha512加密,上python
import hashlib
hash="6500fe72abcab63d87f213d2218b0ee086a1828188439ca485a1a40968fd272865d5ca4d5ef5a651270a52ff952d955c9b757caae1ecce804582ae78f87fa3c9"
for i in range(90000000):
pwd1=hex(i)
pwd1=pwd1[2:]
h1=hashlib.sha512(pwd1.encode('utf-8')).hexdigest()
if h1==hash:
print(hex(i))
break
#0xf20a
第四部分加密
main_hash(a1 + 38, 4LL);
if ( v8 == 32 )
runtime_memequal(v3, (__int64)"ff6e2fd78aca4736037258f0ede4ecf0", 32LL, 32);
main_hash()函数
__int64 __usercall main_hash@<rax>(__int64 a1, __int64 a2)
{
const char *v2; // rdi
__int128 v4; // [rsp+18h] [rbp-78h]
__int64 v5; // [rsp+28h] [rbp-68h]
__int128 v6; // [rsp+48h] [rbp-48h] BYREF
char v7[32]; // [rsp+58h] [rbp-38h] BYREF
__int128 v8; // [rsp+78h] [rbp-18h]
*(_QWORD *)&v4 = runtime_stringtoslicebyte((__int64)v7, a1, a2);
crypto_md5_Sum(v4);
v6 = v4;
v8 = 0LL;
*(_QWORD *)&v8 = runtime_convT2Enoptr((__int64)&unk_524360, (__int64)&v6);
*((_QWORD *)&v8 + 1) = v4;
fmt_Sprintf(v2);
return v5;
}
可知是md5加密
网上找md5在线解密,但是没有找到一个可以正确解密出来的,直接用python写了(自己写不出来,还是wp里面的)
解密
import itertools
import string
import hashlib
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
flag = md5crash("ff6e2fd78aca4736037258f0ede4ecf0")
print(flag)
#f940
最后的flag是
GKCTF{87f645e9-b628-412f-9d7a-e402f20af940}
这绝对是我做过最难的题目了
还发现一个奇怪的事情,第一次ida64打开该文件时,main_check函数里面四个runtime_memequal()函数都没有参数,但是第二次再打开,它又突然显示出来了所有的参数。
参考wp: