Pwnium write-up #1 (Crackme Fast)

This is a quick write-up of the “Crackme Fast” challenge from Pwnium CTF

Let’s start at the beginning. The challenge stated that you had to download a file from a server, crack that application (retrieve a password from it) and submit it to the authentication server altogether in under 2 seconds. This means the moment the file is downloaded, you have under 2 seconds to finish the challenge to retrieve the flag.

Let’s start with the file itself. When I downloaded the file it saved as “4617155699eb1d2b7a17f7a5bdda27b4.bin”, I then opened it in Notepad++ and xvi32. Notepad++ told me it was some kind of uncompressed archive format containing a single executable. How did I know?

And at the bottom of the file:

To make the header of the archive format and the executable header more identifiable, here’s what the header looks like from xvi32:

What is “LPCK”? Honestly, I haven’t a clue. I’ve googled to see if it’s some magic key for an archive format, but I haven’t been able to turn up anything. This might sound idiotic, and maybe I need to know more about compression formats, but since it’s uncompressed and since the end of the file doesn’t seem to contain any archive format data, it is irrelevant for our purposes.

Going off of the xvi32 screenshot, we can see that the executable’s data begins at base+0×110, the string “MZ” indicates the beginning of PE header data (“MZ” is 0x5A4D, which is the e_magic value of the IMAGE_DOS_HEADER struct for PE format executables). So, knowing that there’s nothing to shave off of the end, we only need to shave off the beginning.

Click the button and presto!

Now we save the file as “4617155699eb1d2b7a17f7a5bdda27b4.exe” and we have a valid, runnable, debuggable executable.

With that in mind, let’s move on to the actual crack.

The password validation function is at:
.text:00401334 password_validator proc near

I found that out by looking at the list of strings, it’s not difficult to find.

If we double-click that string and xref it, we’re lead to this function:

I’ve renamed some variables to be more helpful, it’s not all that difficult of a concept to grasp, though, so it shouldn’t be too much of an aid.

You’ll notice a couple things:

“xorPass” is just a copy of the global “xor_key”, and it only xors against 1, not user input. This means that what you input does not change the final result of that data, which is very good, since it means we won’t have to breakpoint anything and step through to complete the challenge.

The second thing is that the key length is 8, this is known because the program takes the length of your input from puts and compares it against the number ’8′.

Let’s check out that xor_key value.

You’ll notice they’re all DWORD values (4-byte, 32-bit integers) rather than 1-byte, despite the fact that they could be 1-byte without any problem. Not sure why they chose to do this, but treating them as 1-byte integers would cause errors later on, so remember it.

Back to the code:

 C++ |  copy code |? 
1
for ( i = 0; i < strlen(input) && (xorPass[i] ^ 1) == input[i]; ++i )

xorPass[i] xors itself against the number 1. This means you can easily retrieve the password if the values are known (and they are, screenshot above) by xoring them in any standard calculator.

0×68 ^ 1 = 0×69 (i)
0x6A ^ 1 = 0x6B (k)
0×66 ^ 1 = 0×67 (g)
0×54 ^ 1 = 0×55 (U)
0x4C ^ 1 = 0x4D (M)
0×44 ^ 1 = 0×45 (E)
0x4D ^ 1 = 0x4C (L)
0x4A ^ 1 = 0x4B (K)

So the password is: ikgUMELK

Result:

 txt |  copy code |? 
1
C:\ctf>4617155699eb1d2b7a17f7a5bdda27b4.exe
2
Password :
3
ikgUMELK
4
Good Boy ! Send That pass to server to get the Flag

If you try submitting this answer to the server, however:
http://41.231.53.44:9393/check.php?p=ikgUMELK

You’ll be greeted with this:
Too late !

Now remember the qualifications for this challenge. You have to download and crack this executable in under 2 seconds. What does this mean?

We have to automate this process!

Now please, please keep in mind that this is a capture the flag contest. The code I wrote here is exactly what I used, because I needed to finish it as quickly and not as cleanly as possible. The code is ugly, but it was the fastest code I could write to get the job done.

 C++ |  copy code |? 
01
#include "stdafx.h"
02
#include <Windows.h>
03
 
04
#pragma comment(lib, "urlmon")
05
 
06
#define XORPASS_SIZE 8
07
 
08
CHAR szDecodedPass[XORPASS_SIZE + 1];
09
 
10
BOOL SubmitData() 
11
{
12
	char szFullUrl[MAX_PATH] = {0};
13
 
14
	sprintf_s(szFullUrl, "http://41.231.53.44:9393/check.php?p=%s", szDecodedPass);
15
 
16
	return SUCCEEDED(URLDownloadToFileA(NULL, szFullUrl, "C:\\ctf\\result.txt", 0, NULL));
17
}
18
 
19
BOOL DownloadFileToDisk()
20
{
21
	return SUCCEEDED(URLDownloadToFile(NULL, _T("http://41.231.53.44:9393/"), _T("C:\\ctf\\hackme.bin"), 0, NULL));
22
}
23
 
24
BOOL ObtainDecodeInformation()
25
{
26
	BOOL bReturn = FALSE;
27
 
28
	memset(szDecodedPass, 0, sizeof(szDecodedPass));
29
 
30
	HANDLE hFile = CreateFileA("C:\\ctf\\hackme.bin",
31
		GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
32
 
33
	if(hFile != INVALID_HANDLE_VALUE) {
34
		DWORD dwFileSize = GetFileSize(hFile, NULL), dwBytesRead = 0;
35
 
36
		printf("File Size: 0x%X\n", dwFileSize);
37
 
38
		PBYTE pbFileBuffer = new BYTE[dwFileSize];
39
 
40
		if(ReadFile(hFile, pbFileBuffer, dwFileSize, &dwBytesRead, NULL)) {
41
			DWORD dwKeyPosition = 0x1310; // Header + Offset to DWORD array
42
 
43
			for(DWORD i = 0; i < XORPASS_SIZE; i++) {
44
				printf("XOR KEY [%i]: 0x%X\n", i, ((DWORD*)(pbFileBuffer + dwKeyPosition))[i]);
45
 
46
				szDecodedPass[i] = (((DWORD*)(pbFileBuffer + dwKeyPosition))[i] ^ 1);
47
			}
48
 
49
			printf("Password: %s\n", szDecodedPass);
50
 
51
			bReturn = TRUE;
52
		}
53
 
54
		delete[] pbFileBuffer;
55
 
56
		CloseHandle(hFile);
57
 
58
	} else {
59
		printf("Unable to open file!\n");
60
	}
61
 
62
	return bReturn;
63
}
64
 
65
int _tmain(int argc, _TCHAR* argv[])
66
{
67
	if(DownloadFileToDisk()) {
68
		if(ObtainDecodeInformation()) {
69
			if(SubmitData()) {
70
				printf("Success!!!\n");
71
			} else {
72
				printf("Failed to submit data!\n");
73
			}
74
		} else {
75
			printf("Failed to ObtainDecodeInformation!\n");
76
		}
77
	} else {
78
		printf("Failed to DownloadFileToDisk!\n");
79
	}
80
 
81
	return 0;
82
}
83

The only part here worth touching upon is this bit:

 C++ |  copy code |? 
01
02
DWORD dwKeyPosition = 0x1310; // Header + Offset to DWORD array
03
 
04
for(DWORD i = 0; i < XORPASS_SIZE; i++) {
05
	printf("XOR KEY [%i]: 0x%X\n", i, ((DWORD*)(pbFileBuffer + dwKeyPosition))[i]);
06
 
07
	szDecodedPass[i] = (((DWORD*)(pbFileBuffer + dwKeyPosition))[i] ^ 1);
08
}
09
 
10
printf("Password: %s\n", szDecodedPass);
11

To find the offset to the xor_key in the bin (remember that pesky LPCK header? What about the offset to the xor_key itself?) I used byte searching in IDA and xvi32.
Simply go to the xor_key variable, highlight it and click hex view.

Select a few of the bytes, and search for it in xvi32 (Search -> Find)

and then…

Ta-da!

So we run the application, and here is the final output:

 txt |  copy code |? 
01
02
C:\ctf>pwnium_crackmefast
03
File Size: 0x6DAD
04
XOR KEY [0]: 0x52
05
XOR KEY [1]: 0x42
06
XOR KEY [2]: 0x53
07
XOR KEY [3]: 0x6A
08
XOR KEY [4]: 0x50
09
XOR KEY [5]: 0x74
10
XOR KEY [6]: 0x33
11
XOR KEY [7]: 0x7A
12
Password: SCRkQu2{
13
Success!!!
14

And the flag:
Pwnium{81bcb0bb77a77af8a37c2567a5f2f0a7}

Happy belated 4th of July everyone!

I’m doing a write-up soon about the “re300″ challenge which included a 64-bit ELF binary on Linux, and GDB debugger, which was an entirely new experience for me. Fun times.
So yeah, hang on to the edge of your seat for that.

Note:
If you want to take a look at the files, I’ve included the bin I’ve used for this post below:
http://s0beit.me/ctf/pwnium/1/crackmefast/4617155699eb1d2b7a17f7a5bdda27b4.bin

6 comments

  1. mev says:

    I found a bit on LPCK: https://github.com/damianmoore/samsung-bios-check/blob/master/samsung_extract_archive.py

    It is some sort of PE archive, used there for Samsung BIOS files. No idea what it’s properly called, and that’s the only example of it’s use I’ve seen.

    • s0beit says:

      Interesting! If it’d have been more complex (particularly compressed, encoded or encrypted) it might have raised the difficulty level because it’s not really my area, and I’d have had to look up this obscure thing and reverse the process, what a horror that would have been.

      Thankfully that wasn’t the case. It’s still nice to know, though!

  2. Enoch Ayeh says:

    Interesting :D very nice write-up could not get to this is there any other ways to identify executable sections in a binary and also how did you crack the xor key,did you write a partial keygen?

    • s0beit says:

      Nah. The first URL which is the EXE link generates a new EXE with a new key every time. So you just have to create a method of decoding the XOR key directly after download (which is really the point of this post).

      I don’t think I mentioned that the key is different every time, though. I forget. A keygen won’t help, the server generates the key, you have no way of knowing what it will be ahead of time.

  3. cub's blog says:

    [...] unrelated: i came across s0beit’s write-up back in July on a CTF cracking problem which is really interesting/in-depth if you’re into [...]