Crackmesone

My write-up for crackmesone ctf first challenge. I never have time to finish Flareon, this was even worse with only 1 week! Fun challenge nevertheless!

CryptPad

That one is a notepad-like exe that has an encrypted file containing the flag. The Cryptpad can encrypt (and not decrypt) files for us. Taking a look in IDA, this function stood out to me as being encryption related:

char __stdcall sub_4014EB(_BYTE *a1, int a2, int a3)
{
  unsigned int v3; // ecx
  _BYTE *v4; // esi
  int v5; // eax
  char result; // al
  _BYTE *v7; // esi
  _BYTE *v8; // edi
  DWORD v9; // ecx
  int v10; // edx
  int v11; // ecx
  _BYTE *v12; // esi
  int v13; // ecx
  int v14; // ebx
  int v15; // ebx
  _BYTE *v16; // esi
  int v17; // eax
  int v18; // ecx
  char v19; // dl
  _BYTE *v20; // edx
  int v21; // eax
  int v22; // ebx
  int v23; // ecx
  int v24; // ecx
  char v25; // dh
  char v26; // dl
  _BYTE *v27; // esi
  _BYTE *v28; // edi
  DWORD v29; // ecx
  int v30; // edx
  _BYTE *v31; // edi
  _BYTE *v32; // [esp-8h] [ebp-Ch]
  int v33; // [esp-4h] [ebp-8h]

  if ( a3 )
  {
    if ( a3 != 1 )
      return MessageBoxA(0, 0, 0, 0);
  }
  else
  {
    v3 = *(_DWORD *)&a1[a2 - 1];
    v4 = &a1[a2 - 1 - v3];
    qmemcpy(byte_4024C5, v4, v3);
    v5 = *((_DWORD *)v4 - 1);
    *((_DWORD *)v4 - 1) = 0;
    a2 = v5;
  }
  v7 = a1;
  v8 = byte_4024C5;
  v9 = NumberOfBytesWritten;
LABEL_6:
  v10 = 0;
  do
  {
    *v7++ ^= *v8++;
    if ( ++v10 == 8 )
      goto LABEL_6;
    --v9;
  }
  while ( v9 );
  v11 = 256;
  do
  {
    byte_403795[(unsigned __int8)-(char)v11] = -(char)v11;
    --v11;
  }
  while ( v11 );
  v12 = &unk_403695;
  v13 = 256;
  v14 = 0;
  do
  {
    if ( v14 >= 8 )
      v14 = 0;
    *v12++ = byte_4024C5[v14++];
    --v13;
  }
  while ( v13 );
  v15 = 0;
  v16 = v12 - 256;
  v17 = 0;
  v18 = 256;
  do
  {
    LOBYTE(v15) = byte_403795[v17] + v16[v17] + v15;
    v19 = byte_403795[v17];
    byte_403795[v17] = byte_403795[v15];
    byte_403795[v15] = v19;
    ++v17;
    --v18;
  }
  while ( v18 );
  v20 = a1;
  v21 = 0;
  v22 = 0;
  v23 = a2;
  do
  {
    v33 = v23;
    v24 = (unsigned __int8)(v21 + 1);
    v32 = v20;
    v25 = byte_403795[v24];
    LOBYTE(v22) = v25 + v22;
    v26 = byte_403795[v22];
    byte_403795[v24] = v26;
    byte_403795[v22] = v25;
    LOBYTE(v24) = byte_403795[(unsigned __int8)(v25 + v26)] ^ a1[v21];
    v20 = v32;
    v32[v21++] = v24;
    v23 = v33 - 1;
  }
  while ( v33 != 1 );
  v27 = a1;
  v28 = byte_4024C5;
  v29 = NumberOfBytesWritten;
LABEL_20:
  v30 = 0;
  do
  {
    result = *v28 ^ *v27;
    *v27++ = result;
    ++v28;
    if ( ++v30 == 8 )
      goto LABEL_20;
    --v29;
  }
  while ( v29 );
  if ( a3 == 1 )
  {
    v31 = &a1[a2 - 13 + RandomBufferLength];
    *(_DWORD *)v31 = a2;
    v31 += 4;
    qmemcpy(v31, byte_4024C5, 8u);
    v31[8] = 8;
    return RandomBufferLength + a2;
  }
  return result;
}

This is actually more readable using IDA's graph mode. What this does is: XOR8( RC4( XOR8( plaintext ) ) ) Where XOR8 is just a XOR with an 8 byte key. When a3 == 1 we're in encryption mode and we use the static key: byte_4024C5 = DE BC 0A 89 67 45 23 01

And after the encryption, we append a footer to the file: [ original_size (4 bytes) ] [ key (8 bytes) ] ( copied from byte_4024C5) [ 8 ]

This caught my eye in particular when comparing the hex of the flag.enc with one of aaaa I generated:

And:

The 3 null bytes showing up in the footer caught my interest.

The 06 right before the 3 null bytes as well: I encrypted 5 a's and a null byte so size 06 meaning our flag is size 1C if we follow that logic. We can write a python script to parse out the footer and apply xor8 rc4 xor8 with our key and decrypt flag.enc:

We end up with: CMO{r0ll_y0ur_0wn_b4d_c0d3}

Last updated