Skip to main content

#LabyREnth CTF - Windows track no. 1 - AntiD.exe

In this task we have to reverse file called 'AntiD.exe'. After first examination of this, it looks to be simple PE32 executable, packed with UPX. Unfortunately we can't decompress it using UPX tool, so I started to unpack it manually. First thing to notice is that in PE Optional Header - DllCharasteristics is set to 8140, which means that DLLs in this executable can move around a bit (I'm usually using programs like 'CFF Explorer' or something similar to check this things out). I've changed this header to 8100, what actually terminated this behaviour ;)

To decompress this .exe I personally used x64dbg and Scylla, but the tool doesn't matter at all - it could be any runtime debugger and ImpRec I suppose. What we need to do is stop program execution at Entry Point of AntiD.exe, and run exactly one instruction : pushal - in my case, as you can see on image below (but You can also see this as PUSHAD in OllyDbg, or any other debugger).

After executing PUSHAD, You'll notice that value of ESP register had changed. We have to right click on this value, and click Follow in Dump. Then in the Dump Window of our debugger we need to check first 4 bytes and right-click again to set a Hardware Breakpoint on access. Then we simply run our program and wait for break to occur. We should land exactly in this place:

If we pass through jmp antid.71647 instruction, our executable will be already unpacked in process memory. What we must do now, is to dump this process from memory to another .exe and correct IAT (Import Address Table). Both this things can be easily done by 'Scylla', which is integrated in x64dbg, but You can use OllyDump, then ImpRec as well.
The last thing to change is OEP in the header of our new file. After quick examination in IDA I decided to redirect our new entry to address:  0x1380.

Main function looks exactly like on the image above and its quite easy to reverse. First puts "Figure the key out: " on the console output, then gets input from user and pass that to function which I called 'Check_Key' in IDA. If that fuction will return 1, the string: "Well done! A+! You get a gold star!\n" will be printed to us, else we'll see: "You wrong, why you so wrong at this?\n". But we still don't have a flag. We have to dig deeper - next step is to examine function "Check Key". Here's my version of it's disassembly:
char __cdecl Check_Key(char *Str)
  char result; // al@5
  char v2; // [sp+0h] [bp-38h]@2
  signed int i; // [sp+4h] [bp-34h]@2
  char v4; // [sp+8h] [bp-30h]@4
  char v5; // [sp+8h] [bp-30h]@6
  char v6; // [sp+8h] [bp-30h]@8
  char v7; // [sp+8h] [bp-30h]@10
  int v8; // [sp+8h] [bp-30h]@12
  unsigned __int8 hash_tab[40]; // [sp+Ch] [bp-2Ch]@1

  hash_tab[0] = 0x8Cu;
  hash_tab[1] = 0xF1u;
  hash_tab[2] = 0x53;
  hash_tab[3] = 0xA3u;
  hash_tab[4] = 8;
  hash_tab[5] = 0xD7u;
  hash_tab[6] = 0xDCu;
  hash_tab[7] = 0x48;
  hash_tab[8] = 0xDBu;
  hash_tab[9] = 0xC;
  hash_tab[10] = 0x3A;
  hash_tab[11] = 0xEEu;
  hash_tab[12] = 0x15;
  hash_tab[13] = 0x22;
  hash_tab[14] = 0xC4u;
  hash_tab[15] = 0xE5u;
  hash_tab[16] = 0xC9u;
  hash_tab[17] = 0xA0u;
  hash_tab[18] = 0xA5u;
  hash_tab[19] = 0xC;
  hash_tab[20] = 0xD3u;
  hash_tab[21] = 0xDCu;
  hash_tab[22] = 0x51;
  hash_tab[23] = 0xC7u;
  hash_tab[24] = 0x39;
  hash_tab[25] = 0xFDu;
  hash_tab[26] = 0xD0u;
  hash_tab[27] = 0xF8u;
  hash_tab[28] = 0x3B;
  hash_tab[29] = 0xE8u;
  hash_tab[30] = 0xCCu;
  hash_tab[31] = 3;
  hash_tab[32] = 6;
  hash_tab[33] = 67;
  hash_tab[34] = 0xF7u;
  hash_tab[35] = 0xDAu;
  hash_tab[36] = 0x7E;
  hash_tab[37] = 0x65;
  hash_tab[38] = 0xAEu;
  hash_tab[39] = 0x80u;
  if ( strlen(Str) == 16 )
    v2 = 0;
    for ( i = 0; i < 40; ++i )
      v4 = Str[i] ^ 0x33;
      if ( check_1() )
        return 0;
      v5 = v4 + 0x44;
      if ( check_2() )
        return 0;
      v6 = v5 ^ 0x55;
      if ( check_3() )
        return 0;
      v7 = v6 - 0x66;
      if ( check_4() )
        return 0;
      v8 = (unsigned __int8)(v7 ^ v2);
      if ( v8 != hash_tab[i] )
        return 0;
      v2 += v8;
    result = 1;
    result = 0;
  return result;
From now we can see exactly what is going on here. Our input has to have 16 characters to move on. If it is indeed, then our flag should be produced in a loop. I didn't care about all that checking functions, beacuse I decided to write my own decoder than debugging the flag on runtime. Here's the code to do the job:

using namespace std;

int main(void)
 unsigned char v2 = 0;
 unsigned char tab[] = 
     { 0x8C,0xF1,0x53,0xA3,0x08,0xd7,0xDC,0x48,0xDB,0x0C,
       0xCC,0x03,0x06,0x43,0xF7,0xDA,0x7E,0x65,0xAE,0x80 };
 for (char j = 0; j < 40; j++)
  x = 0;
  for (char i = 0; i < 0xff; i++)
   x = i ^ 0x33;
   x = x + 0x44;
   x = x ^ 0x55;
   x = x - 0x66;
   x = x ^ v2;
   if (x == tab[j]) 
    printf("%c", i);
  v2 = x + v2;
 cout << endl;
 return 0;
And that's all - it will print our flag! 
Link to the file: AntiD.exe


Popular posts from this blog

#IceCTF - Strong Feeling

You can download ELF here: ------------------------> link
To get a flag in this one, easiest way I think is to bruteforce it! After quick look of executable in decompiler we can see that program outputs different strings every time we input a proper flag character to it. The best way to check that (knowing that flags in that CTF looks like "IceCTF{xxx}") is to  input 'I' first, then "Ic", then "Ice", etc. The strings in ELF aren't obfuscated, so we can just count it to figure out number of characters in the flag. The only thing that has to be done now is bruteforcer itself. I wrote something like that:
#include &ltstdio.h&gt #include &ltstdlib.h&gt #include &ltcstring&gt using namespace std; int main(void) { char *flag = new char[32]; char *path = new char[128]; char *buffer = new char[128]; char *buf2 = new char[128]; FILE *plik; for (int i = 0; i < 32; i++) for (char j = 0x21; j < 0x7f; j++) { …

Gynvael's Task 1

This is short solution of task provided by Gynvael Coldwind on one of his online streams (here's a link ----> ) . We have to decrypt message: 
1f9111 1799 0790001226d8 0a9e1e5c3ada 1f 099e195e 0a97075a21dac1 0a9710 199e075131d3 1199 12961350
The message is in english, XOR-ed by 160bit key. Every word is encrypted separately.  My approach to that is simply to assume that the single letter in the message has to be either 'a' or 'i'. If one of them is correct then XORing first letter of each word with correct key value (obtained by xoring 0x1f with 'a' or 'i') should print out english letter. In this case letter 'a' worked brilliantly ;-) Next step was to assume that first word has to be "and". The rest is even easier.
And here's the code to decrypt them all, and retrieve a key:

#include &ltcstdio&gt #include &ltvector&gt int main() { int key[7] = {0}; std::vecto…