CSAWCTF 2013 Reversing

Published September 24, 2013 By Joe Savage

I had a bunch of fun recently attempting some of the CSAW CTF 2013 challenges. I didn't spend as much time on them as I would have liked, but I passed through the first four reversing challenges and thought it might be nice to step through my solutions to these four in a blog post.

If you haven't already tried some of the challenges yourself, I'd recommend you do so before reading ahead, but if you're stumped or just don't feel like trying for yourself, I've tried to outline my solutions in a somewhat detailed manner so that the steps could be followed by a newcomer to these kinds of things.

DotNetReversing (100pt)

DotNetReversing.exe: this is the first, though in my opinion not the easiest, challenge in the series. Pop the application open (in a VM or otherwise), and you'll see a prompt to enter a passcode for the flag.

First things first, for .NET reversing I can't recommend any tool more than .NET Reflector — it's immensely useful. Upon opening the executable in reflector, head straight to the main function and you'll see the following.

private static void Main(string[] args)
	Console.WriteLine("Greetings challenger! Step right up and try your shot at gaining the flag!");
	Console.WriteLine("You'll have to know the pascode to unlock the prize:");
	long num = Convert.ToInt64(Console.ReadLine());
	long num2 = 0xc5ec4d790L;
	long num3 = 0xf423abdb7L;
	if ((num ^ num2) == num3)
		Console.WriteLine("Incorrect, try again!");
		byte[] iV = new byte[] { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
		byte[] array = new byte[0x10];
		BitConverter.GetBytes(num).CopyTo(array, 0);
		BitConverter.GetBytes(num).CopyTo(array, 8);
		string s = "ls/5RTxwflDrqr5G8pO9cQ1NlgQcFjcJj9x4z7oIhlfY4w42GAFqKbyzwqHAZQBZa5ctysKKWIbTgU2VxoRYohxCbPyV6sEU/tn+sIxNg6A/r5OJnIMqTs0seMrzWh5J";
		Console.WriteLine(DecryptStringFromBytes_Aes(Convert.FromBase64String(s), array, iV));
	catch (Exception exception)
		Console.WriteLine("ERROR!!! darn. huh? how did I get here? Hmm, something must have gone wrong. What am I doing?", exception);
	Console.WriteLine("press key to continue");

From here, this challenge isn't too difficult. You can see there's some code that decrypts an encoded string 's', and this gets executed whether or not the program decides we've inputted the "correct" value — as such, our input is clearly important to the decryption process.

The 'if' statement that seems to be interpreting our value as correct or incorrect is checking that (num ^ num2) == num3, where num2 is 0xc5ec4d790 and num3 is 0xf423abdb7. So it appears that our number needs to XOR with num2 to create num3. As such, we can XOR num2 with num1 to find num!

This yields the result of 0x31CFE6A27, which is 13371337255 in decimal. And so, inputting 13371337255 into the program will yield the flag: flag{I'll create a GUI interface using visual basic...see if I can track an IP address.}.

reversing1 (100pt)

With the first challenge complete, we go ahead to the second, which in my opinion is even easier than the first! If you pop open the program, you should see a bunch of garbled characters — Hmm...

This probably means that the message has been obfuscated or encrypted in some way. Let's go ahead and open up the program in a debugger such as OllyDbg and see what's really happening here.

Oh, woah, that was easy! It turns out, on further source inspection, that the application is calling IsDebuggerPresent to check whether a debugger is attached or not, and if a debugger is attached then it yields the flag: flag{this1isprettyeasy:)}.

bikinibonanza (150pt)

Ok, 100 point challenges out of the way, let's start frying some bigger fish with this 150 point challenge. On opening up the application, you'll see an image of the sea with a saw (CSAW!), and a textbox with a "Submit" button. It seems like we need to enter some kind of code to get the flag out of this bad boy.

Upon inspecting the file, it appears to be a .NET executable, so let's pop open reflector! Woah, what's with all these horribly named functions?!

It turns out that pretty much all the functions and objects are named eval_??, and this makes the code annoying to trace. Upon first viewing this code I really didn't want to trace through, but after seeing no easy-access signs of the flag, I was left with little choice. Reflector gives us a bit of a break here, however, as you can click on names of methods and objects in code snippets to see what they're referring to. In this case, if we trace through the code from the entry point, we can see that the Click event of something is being linked to a function, aha! Following this to eval_??(Object, EventArgs), we can see that it's doing some things with the current hour of the time and with the string "NeEd_MoRe_Bawlz"...

Tracing through the code really is the key here. Once you've figured out what it's doing (which mainly consists of getting over the interesting naming conventions), you can create your own application that mimics 'strB' in the reflector output, which is what our input gets compared to. Through some Copy & Paste action, I worked up the following solution:

static void Main(string[] args)
	string text1 = "NeEd_MoRe_Bawlz";
	int num1 = Convert.ToInt32(string.Format("{0}", DateTime.Now.Hour + 1));

	string strB = "";
	int num2 = 0;
	if (0 < text1.Length)
			char ch = text1[num2];
			int num = 1;
			if (1 < num1)
					ch = Convert.ToChar((new int[] { 
						2, 3, 5, 7, 11, 13, 0x11, 0x13, 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35, 
						0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59, 0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71
					 }[num] ^ Convert.ToInt32(ch)));
				while (num < num1);
			strB = strB + ch;
		while (num2 < text1.Length);

	byte[] bytes = Encoding.ASCII.GetBytes(strB);
	Console.WriteLine(BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(bytes)).Replace("-", ""));

Using the key generated by this program, the comparison we saw in the reflector output succeeds and the flag — which is time-dependent — is outputted.

reversing2 (200pt)

With 350 points in the bag so far, it's time to grab another 200. Let's go ahead and open up the program, and...

Oh, it's crashed! Well, that seems odd. Let's open it up in OllyDBG and see what happens.

Aha, it didn't crash this time, and we got some output! The output looks kind of garbled though — much like 'csaw2013reversing1.exe' did before we opened it in the debugger. It seems like the flag is obfuscated or encrypted in some way, so I opened up IDA to get a better idea of the flow of the application.

It looks like some kind of comparison is taking place, and then a conditional jump occurs. When we ran the program in our debugger, we hit that conditional jump and skipped a whole bunch of code. What's more, if we actually take a look at the code we skipped, it contains XOR instructions and loops, which look likely candidates for making our flag readable! Let's go ahead and re-run the program, making sure that the JNZ doesn't occur (you can run through and set the ZF manually before the instruction if you'd prefer, but I NOP (0x90) the jump out completely below) and seeing what happens.

Aaand... oh, an empty message box. Well that's kind of disappointing. It's also kind of convenient that our output happened to end up as nothing though, isn't it? A little too convenient for my liking. A little further from that conditional jump is the place where the MessageBoxA call takes place — let's restart the program and go ahead and set a breakpoint right after the message box text is pushed to the stack so we can take a look for ourselves.

For me the "ESI" register was pushed as the message box text, so let's go follow it to see where it leads.

Aha, we've struck gold! Upon following the register, the following is shown in the dump.

We have the flag! Note that the first byte of the string to be outputted is 0x00 — this is a NUL ('\0') byte, which indicates the end of a null-terminated string, and that's why our message box wasn't outputting the flag properly! We can just copy the flag from the dump here, but for the sake completeness I went ahead and changed the NUL byte (0x00) to a space character (0x20) to get the satisfaction of seeing the message box with our final flag, flag{number2isalittlebitharder:p}.