Monday, September 23, 2013

CSAW CTF Quals - Reversing (100, 150, 300) write-ups

The CSAW CTF qualifier was a really cool "entry-level" CTF with some nice challenges. We finished in the 12th place with !SpamAndHex. Great team! Here is a write-up for the reverse 100, 150 and 300 tasks.

Reverse 100

This was the "CSAW Reversing 2013 1" challenge, and it was quite simple. When we start the application, this is what we see:


Once looking at the binary in IDA, we can clearly see that it calls "IsDebuggerPresent". 


This does quite the opposite of what we would expect, since usually we want to avoid the detection of the debugger. Here however, we will only see the correct flag if we launch the application in the debugger. Here is the flag:


I'm only doing a write-up for this task because there is no write-up on ctftime and I have a mild case of OCD.

Reverse 150 (bikinibonanza)



This was a much more interesting challenge, especially because I don't have much experience with reversing .NET binaries. I used JetBrains dotPeek to look at the source, which was obfuscated. Here is the most important part of the source:
this.eval_ᜀ("NeEd_MoRe_Bawlz", Convert.ToInt32(string.Format("{0}", (object) (now.Hour + 1))), ref strB);
if (string.Compare(text.ToUpper(), strB) == 0)
{
    this.eval_ᜂ.Text = "";
    Form1 form1 = this;
    int num1 = 107;
    int num2 = (int) form1.eval_ᜀ(num1);
    form1.eval_ᜀ((char) num2);
    this.eval_ᜁ();
    this.eval_ᜂ.Text = string.Format(this.eval_ᜂ.Text, (object) this.eval_ᜀ(resourceManager));
    this.eval_ᜃ.Image = (Image) resourceManager.GetObject("Sorry You Suck");
}
else
{
    this.eval_ᜃ.Image = (Image) resourceManager.GetObject("Almost There");
    this.eval_ᜀ();
}
The strings are a bit misleading here. The "Sorry You Suck" message actually refers to the image that says "YOU DID IT". This image was retrieved from the binary, but it can also be found using dotPeek:


So we need to get the execution to the first part of the if branch. I know, we should patch the binary! I never patched a .NET binary before though. Here is what I did.

I looked at the binary in IDA and searched for the branch that needs changing. I also made sure to change the settings so that the byte code is visible. This is how I will later find the correct sequence of bytes in the binary.


The branch is done by the brtrue.s instruction, so we should change that to brfalse.s. A quick google query shows us the correct byte code to use (2C instead of 2D). Then I patched the binary with my favorite hex editor. Here I made sure that there is only one occurence of the byte sequence that I am changing. After running the patched application, I got a key right away!



Reverse 300 (crackme)

For this challenge we received both a binary and an IP/port pair where the service runs, which means we couldn't cheat with patching any more. The service asked for a key and checked it. The task here was to write a simple keygen. Fortunately the algorithm that checks the key wasn't very complicated and it was prone to a very simple attack.

Here is the decompiled C code that asks for the key:


sub_8048EA0 could be renamed to "check_key". Here is the decompiled code that was inside:
signed int __cdecl sub_8048EA0(unsigned __int8 *a1)
{
  int v1; // ST0C_4@3
  unsigned __int8 v3; // [sp+3h] [bp-Dh]@1
  signed int v4; // [sp+4h] [bp-Ch]@1
  unsigned __int8 *v5; // [sp+8h] [bp-8h]@2

  v3 = *a1;
  v4 = 1337;
  if ( *a1 )
  {
    v5 = a1;
    do
    {
      v1 = 32 * v4 + v3;
      v3 = (v5++)[1];
      v4 += v1;
    }
    while ( v3 );
  }
  return v4;
}
Partly because I was lazy and partly because I wanted to finish the task as quick as possible, I decided to brute-force the key. We can see that the algorithm computes a DWORD value starting at 1337. Later it will be compared with a pre-defined value, which is 0xEF2E3558. We can also see that each new byte in the key is added to the current "hash" after it was multiplied by 32.

This is how we can re-write the function in python:
hash = 1337
for d in data:
    v1 = 32 * hash + ord(d)
    hash += v1
   
    # to emulate 32-bit integers
    if hash > 0xffffffff:
        hash &= 0xffffffff
We can write a script that starts creating random keys of any length and see how close the hash is to the desired result. I worked with 84 character long hashes for some weird reason that I can't remember. To avoid problems I only generated ASCII keys.

Then I found a key that was really close to the hash we needed, so from there I started modifying the last few bytes by hand so that the results match exactly. After a few tries, this is the key I found:
]#&uN|Jl4@BHBZx%U*5 s$19):>\p9A2K%nwB)3N8/xKF?+8Ygah,cedFRw_$tAUm/2\y(:;P\^=3v_qxqo7
Ugly or not, this key earned us 300 more points :)
Thank you, valued customer!
Your key is: day 145: they still do not realize this software sucks
Thanks for the CSAW organizers for a really neat CTF! We had tons of fun with all the challenges.

No comments:

Post a Comment