The-Old-Norse-Theonym

Reverse Engineering

Given an ELF file and flag.txt.enc. immediately decompile the ELF using ida and got these functions:

main::main

char __fastcall main::main(_QWORD *a1)
{
  __int64 executable_name; // rax
  __int64 v2; // rdx
  char result; // al
  __int64 v4; // rax
  __int128 v5; // [rsp+40h] [rbp-158h]
  __int64 v6; // [rsp+A0h] [rbp-F8h]
  __int64 v7; // [rsp+A8h] [rbp-F0h]
  _DWORD v8[2]; // [rsp+B8h] [rbp-E0h] BYREF
  __int64 v9; // [rsp+C0h] [rbp-D8h] BYREF
  __int128 v10; // [rsp+C8h] [rbp-D0h]
  __int64 v11; // [rsp+F0h] [rbp-A8h]
  __int128 v12; // [rsp+100h] [rbp-98h] BYREF
  __int64 v13; // [rsp+110h] [rbp-88h]
  __int64 v14; // [rsp+118h] [rbp-80h]
  _DWORD v15[2]; // [rsp+120h] [rbp-78h] BYREF
  __int64 v16; // [rsp+128h] [rbp-70h] BYREF
  unsigned int v17; // [rsp+130h] [rbp-68h]
  __int64 v18; // [rsp+140h] [rbp-58h]
  unsigned int v19; // [rsp+14Ch] [rbp-4Ch] BYREF
  __int64 v20; // [rsp+150h] [rbp-48h]
  __int64 v21; // [rsp+158h] [rbp-40h]
  void *v22; // [rsp+160h] [rbp-38h]
  __int64 v23; // [rsp+168h] [rbp-30h]
  _BYTE *v24; // [rsp+170h] [rbp-28h]
  __int64 v25; // [rsp+178h] [rbp-20h]
  _BYTE v26[24]; // [rsp+180h] [rbp-18h] BYREF

  v26[23] = -123;
  v26[22] = 113;
  v26[21] = 70;
  v26[20] = -82;
  v26[19] = -33;
  v26[18] = 3;
  v26[17] = -62;
  v26[16] = 117;
  v26[15] = 98;
  v26[14] = -114;
  v26[13] = 63;
  v26[12] = -26;
  v26[11] = 117;
  v26[10] = -32;
  v26[9] = -76;
  v26[8] = -43;
  v26[7] = 113;
  v26[6] = -112;
  v26[5] = 112;
  v26[4] = 52;
  v26[3] = -84;
  v26[2] = 63;
  v26[1] = -92;
  v26[0] = 41;
  v25 = 24;
  v24 = v26;
  v23 = 1;
  v22 = &unk_4162B9;
  executable_name = main::get_executable_name((__int64)a1);
  v21 = v2;
  v20 = executable_name;
  v19 = 0;
  v18 = os::open(&unk_4162B9, 1, 0, 0, &v19, a1);
  v17 = v19;
  v16 = v18;
  v15[1] = 0;
  v15[0] = 0;
  result = (unsigned __int8)__equal_1905637414496559711(&v16, v15) == 0;
  if ( !result )
  {
    v4 = *a1;
    v14 = a1[1];
    v13 = v4;
    v12 = 0;
    v11 = os::read_dir(v17, -1, v4, v14, &v12);
    v10 = v12;
    v9 = v11;
    v8[1] = 0;
    v8[0] = 0;
    if ( (unsigned __int8)__equal_1905637414496559711(&v9, v8) )
    {
      v7 = *((_QWORD *)&v10 + 1);
      v6 = -1;
      while ( ++v6 < v7 )
      {
        v5 = *(_OWORD *)(v10 + 72 * v6 + 16);
        if ( !(unsigned __int8)BYTE12(*(_OWORD *)(v10 + 72 * v6 + 32))
          && !main::should_skip_file(v5, *((__int64 *)&v5 + 1), v20, v21, (__int64)a1) )
        {
          main::encrypt_file(v5, *((__int64 *)&v5 + 1), (__int64)v24, v25, a1);
          os::remove(v5, *((_QWORD *)&v5 + 1), a1);
        }
      }
      runtime::delete_slice_proc_array___os::File_Info_allocator_runtime::Allocator_loc_runtime::Source_Code_Location_____runtime::Allocator_Error_(
        v10,
        *((_QWORD *)&v10 + 1),
        *a1,
        a1[1],
        &off_4162C0);
      return os::close(v17, a1);
    }
    else
    {
      return os::close(v17, a1);
    }
  }
  return result;
}

Main::encrypt_file

Main::init

Main::crypt

From the main::crypt function we can see the encryption uses RC4, which uses a key to generate a keystream, and then XORs the keystream with the file content.

And from main::main we can see that the encryption key is hardcoded and is stored in an array v26 with 24 bytes.

Since RC4 is a symmetric stream cipher, the same key is used for both encryption and decryption. So using the key from the main::main, we can write a python script.

solver.py:

When we run it, we get the flag:

Flag: WRECKIT60{1278644a3873e8874ea91a544a3cf07dc3f8e39210e847f0f222e16cbc665d2b}

Last updated