文章里分析的是初版,也就是有多解的版本。
直接把程序拉到IDA,分析main函数,伪代码如下
int __cdecl main(int argc, const char **argv, const char **envp) { int v3; // eax unsigned int namelen; // kr04_4 int result; // eax char name[97]; // [esp+8h] [ebp-12Ch] __int16 v7; // [esp+69h] [ebp-CBh] char v8; // [esp+6Bh] [ebp-C9h] char sn[197]; // [esp+6Ch] [ebp-C8h] __int16 v10; // [esp+131h] [ebp-3h] char v11; // [esp+133h] [ebp-1h] name[0] = 0; sn[0] = 0; memset(&name[1], 0, 0x60u); v7 = 0; v8 = 0; memset(&sn[1], 0, 0xC4u); v10 = 0; v11 = 0; v3 = time(0); sub_411AD8(v3); printf(aEnterYourName); scanf(Format, name); namelen = strlen(name) + 1; if ( namelen - 1 < 3 || namelen - 1 > 0x14 ) // 判断name的长度 { printf(aBadName); result = -1; } else { printf(aEnterYourSn); scanf(Format, sn); if ( strlen(sn) == 64 ) // sn长度是否为64 { checksn((int)name, namelen - 1, (int)sn, 64); result = 0; } else { printf(aBadSn); result = -1; } } return result; }
关键函数为checksn,伪代码如下
void __cdecl checksn(int name, unsigned int namelen, int sn, int snlen) { int hash[4]; // [esp+4h] [ebp-70h] char deresult[32]; // [esp+14h] [ebp-60h] char v6; // [esp+34h] [ebp-40h] char aesdecrypt; // [esp+54h] [ebp-20h] if ( namelen >= 3 && namelen <= 0x14 && snlen == 64 ) { if ( hexstrtodata((_WORD *)sn, 64, (int)&v6) != 32 || (aes((int)&v6, 32, (int)&aesdecrypt, (int)&key, 128, 0), RSA_decrypt((int)&aesdecrypt, 32, (int)deresult), deresult[0]) // aes+rsa || deresult[1] != 2 || deresult[15] ) { printf(aBadSn); } else { gethash((const void *)name, namelen, (int)hash); if ( !memcmp(hash, &deresult[16], 0x10u) )// 比较后0x10字节是否相同 printf(aCongratulation); } } }
sn先把hex字符串转成数据,再用AES和RSA解密,需要注意的是解密后的结果是0x20字节,最后比较的字节数只有0x10。
所以需要把解密结果的前0x10字节跟"KCTF"的hash拼接,再加密就可以得到flag了。
aes函数的最后一个参数是判断加解密的标志,改1就是加密。然后从RSA_decrypt函数里提取N和E,分解N得到P Q,算出D就可以了。
RSA的部分,用python实现
rsa_e = 0x10001 rsa_d = 0x390A684CB713378FFD5CCE8C4000B5D6A2BB9F29B63D395E6BE6E9DD941527BD rsa_mod = 0x69823028577465AB3991DF045146F91D556DEE8870845D8EE1CD3CF77E4A0C39 result = pow(0x00025D343CED2E5A3CD5FE94CEA1570014AF58AD4D76D59D8D2171FFB4CA2231, rsa_d, rsa_mod) print hex(result)
00025D343CED2E5A3CD5FE94CEA15700 是前0x10字节
14AF58AD4D76D59D8D2171FFB4CA2231 是"KCTF"的hash
把得到的结果再AES加密就可以了。
flag:6ED8BC1F04D0C360567FB579398265FEEC8B48DC4B804904FEB1AB538C823270
由于初版程序只判断了dereuslt的[0] [1] [15],所以其他位置可以随便填充数据,出现多解。