Sunday, September 29, 2013

No cON Name Facebook CTF qualifiers writeup

With only 3 tasks and no flag submission, the No cON Name Facebook CTF had a slightly unusual qualifier. On Twitter the organizers clarified that the qualifier is "not a CTF". The teams not only had to solve the tasks, they had to create writeups as well. The tasks were interesting, but they were surprisingly easy. I worked alone this time and solved the tasks pretty quickly.

Level 1

This was a web hacking challenge with some client-side JavaScript code to evaluate the key.


Looking at the underlying JavaScript we can see this obfuscated code:




After beautifying the code, this is what we can see:
var _0x52ae = [...];
eval(function (_0x7038x1, _0x7038x2, _0x7038x3, _0x7038x4, _0x7038x5, _0x7038x6) {
    ...
}(_0x52ae[0], 46, 46, _0x52ae[3][_0x52ae[2]](_0x52ae[1]), 0, {}));
The easiest way to deobfuscate this is to change "eval" to "console.log" (in chrome). This will print all of the unpacked JS source. Here is an interesting function:
function encrypt(form) {
    var res;
    res = numerical_value(form.password.value);
    res = res * (3 + 1 + 3 + 3 + 7);
    res = res >>> 6;
    res = res / 4;
    res = res ^ 4153;
    if (res != 0) {
        alert('Invalid password!')
    } else {
        alert('Correct password :)')
    }
}
Any string that produces a numerical value between 62540 and 62544 will be accepted. Finally, this is how the numerical_value is computed:
function numerical_value(str) {
    var i, a = 0,
        b;
    for (i = 0; i < str.length; ++i) {
        b = ascii_one(str.charAt(i));
        a += b * (i + 1)
    }
    return a
}
It is pretty straightforward to forge a key by hand now. This was one that worked for me: "}}zzzzzzzzzzzzzzzzzzzzzzzzzzzyyA". I started adding new letters until I was close to the target value. Then I changed the values that are close to the beginning to fine-tune it. It was a pretty bad idea to start with "z" characters, since there are very few characters with larger ASCII codes. Here is the flag as the result:


Level 2

The task here was to reverse-engineer an Android application package (apk) file. I cheated here and instead of looking at the source I looked at the resource files. There were 16 images that looked like parts of a QR code. It took me roughly 3 minutes to puzzle them together and after reading the QR code I got a flag:


788f5ff85d370646d4caa9af0a103b338dbe4c4bb9ccbd816b585c69de96d9da
Level 3

Level 3 was a 64-bit ELF executable. This is what happens when we launch the application:


I started reversing the ELF binary, but I'm terribly lazy and I wanted to do this as fast as I can, because if I were trying to get into the final with a team time would have mattered. I decided to write a script that tries every character until it is accepted and then continue with the next one. I figured out the first character by hand (it was a space, for some reason this was my third try) and then I knew what to look for in the output. Here is the script, it's pretty short:
from subprocess import Popen, PIPE, STDOUT
flag = ""
while True:
for x in range(32, 128):
p = Popen(['./level.elf'], stdout=PIPE, stdin=PIPE, stderr=PIPE)
stdout_data = p.communicate(input=(flag+chr(x)))[0]
if stdout_data.count("*") > len(flag):
flag += chr(x)
print flag
break
This is what we get when we run the script:


And here is the flag after trying the password:


Interesting challenge. I really should have reversed it instead. I will never be a better reverse engineer if I keep cheating. Next time!

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.