分析BSidesSF CTF 2020中Crypto方向题目
前言
在BSidesSF CTF 2020中有9道Crypto相关的题目,题目整体难度适中,在这里对这9道题目进行一下分析。
chameleon
题目描述:Somebody encrypted our flag and lost the key! Can you decrypt it? We’ve included the encryption utility, it should come in handy!Note: The file was encrypted in the past few months. We don’t have a more specific date.
题目附件:chameleon.exeflag.png.enc
用IDA加载本题的exe程序,发现程序去除了符号表,在main函数的最后可以找到encrypt和decrypt函数,我们跟进encrypt函数看一下:
void __usercall sub_401FC0(const CHAR *a1@)
{
void *v1; // eax
BYTE *v2; // esi
FILE *v3; // eax
DWORD pdwDataLen; // [esp+4h] [ebp-2Ch]
HCRYPTKEY phKey; // [esp+8h] [ebp-28h]
HCRYPTPROV phProv; // [esp+Ch] [ebp-24h]
BYTE pbData; // [esp+10h] [ebp-20h]
char v8; // [esp+11h] [ebp-1Fh]
__int16 v9; // [esp+12h] [ebp-1Eh]
int v10; // [esp+14h] [ebp-1Ch]
int v11; // [esp+18h] [ebp-18h]
int v12; // [esp+1Ch] [ebp-14h]
int v13; // [esp+20h] [ebp-10h]
int v14; // [esp+24h] [ebp-Ch]
int v15; // [esp+28h] [ebp-8h]
v1 = (void *)sub_401EA0(&pdwDataLen);
v2 = (BYTE *)realloc(v1, pdwDataLen + 16);
if ( !CryptAcquireContextA(&phProv, 0, "Microsoft Enhanced Cryptographic Provider v1.0", 1u, 0xF0000000) )
goto LABEL_9;
sub_401A10((int)&v14);
v9 = 0;
pbData = 8;
v11 = 8;
v12 = v14;
v13 = v15;
v8 = 2;
v10 = 26113;
if ( !CryptImportKey(phProv, &pbData, 0x14u, 0, 1u, &phKey)
|| !CryptEncrypt(phKey, 0, 1, 0, v2, &pdwDataLen, pdwDataLen + 8) )
{
LABEL_9:
v3 = _iob_func();
fprintf(v3 + 2, "Encryption failedn");
exit(1);
}
sub_401AA0((int)&v14);
sub_401F50(a1, v2, pdwDataLen);
free(v2);
}
可以看到程序调用了CryptAcquireContextA、CryptImportKey、CryptEncrypt等API,查阅API手册可以看到这里程序使用DES-CBC进行加密,key来自sub_401A10函数,那么我们接下来跟进sub_401A10函数来看一下:
char __usercall sub_401A10@(int a1@)
{
__time64_t v1; // rax
signed int v2; // ecx
unsigned int v3; // esi
v1 = time64(0);
v2 = 0;
do
{
LODWORD(v1) = 29945647 * v1 – 1;
dword_404380[v2++] = v1;
}
while ( v2 351 );
dword_404018 = v2;
v3 = 0;
do
{
LOBYTE(v1) = sub_401870(v2, HIDWORD(v1)) ^ 0x55;
*(_BYTE *)(v3++ + a1) = v1;
}
while ( v3 8 );
return v1;
}
这里看到v1 = time64(0),结合题目描述,看起来像是使用系统时间在设置种子,第一个循环根据种子来设置数组,我们继续跟进sub_401870函数来看一下:
int sub_401870()
{
int v0; // eax
int v1; // eax
unsigned int *v2; // ecx
unsigned int v3; // esi
int v4; // eax
unsigned int v5; // edi
unsigned int v6; // esi
int v7; // eax
unsigned int v8; // esi
unsigned int v9; // edi
int v10; // eax
unsigned int v11; // edi
unsigned int v12; // esi
int v13; // eax
unsigned int v14; // esi
unsigned int v15; // ecx
int v16; // ecx
v0 = dword_404018;
if ( dword_404018 >= 351 )
{
v1 = 175;
v2 = (unsigned int *)&unk_404384;
do
{
v3 = *v2;
v4 = v1 + 1;
*(v2 – 1) = ((*(v2 – 1) ^ (*v2 ^ *(v2 – 1)) & 0x7FFFF) >> 1) ^ dword_40437C[v4] ^ -((*((_BYTE *)v2 – 4) ^ (unsigned __int8)(*(_BYTE *)v2 ^ *((_BYTE *)v2 – 4))) & 1) & 0xE4BD75F5;
if ( v4 >= 351 )
v4 = 0;
v5 = v2[1];
v6 = ((v3 ^ (v3 ^ v2[1]) & 0x7FFFF) >> 1) ^ dword_404380[v4] ^ -(((unsigned __int8)v3 ^ (unsigned __int8)(v3 ^ *((_BYTE *)v2 + 4))) & 1) & 0xE4BD75F5;
v7 = v4 + 1;
*v2 = v6;
if ( v7 >= 351 )
v7 = 0;
v8 = v2[2];
v9 = ((v5 ^ (v5 ^ v2[2]) & 0x7FFFF) >> 1) ^ dword_404380[v7] ^ -(((unsigned __int8)v5 ^ (unsigned __int8)(v5 ^ *((_BYTE *)v2 + 8))) & 1) & 0xE4BD75F5;