ICMTC-2 CTF - Reverse

6 minute read

?

1) OperationQak

This challenge is a straight forward one.

First I tried to load it with pestudio to see if it has any packed sections, interested sections or strings and so but there was nothing. So I uploaded it into IDA.

?

As you can see its a simple app. It first do some prints and initializations. Then it calls a function that seems to be a simple encryption function :

?

So it passes some value to it and returns another. and the comparison is done between my entered value and another value returned from this function, and if they are the same, it’s going to call GetTickCount() function and print the flag as shown in the format of printf. The easy way is to put a break point when it calls strcmp() to read the compared value, then pass it to the program again to avoid reading the returned value from GetTockCount() with a high value:

?

operation_duck_hunt0ted is the value target, and when I pass it with to the program it will print to us the flag.

?

flag : EGCERT{557351812_operation_duck_hunt}

2) SimpleObfuscator

This sample is a .NET app, nothing special about it.

?

But when I load it into dnsSpy, it seems to be very unorganized, like it’s obfuscated (actually, it’s pretty obvious that it’s obfuscated from its name : ).

?

When I deal with an obfuscated .NET app, it comes to my mind directly the NETReactorSlayer tool. So I uploaded the sample into it and tried to deobfuscate the sample.

?

Then analyzing the app again, the strings are more clear now. The only part that’s important to us is this :

?

the b variable contains this string :

?

If the comparison works, it will print to us the flag, so passing this string to it :

?

flag : EGCERT{n00b_0bfuscat0r_aopbrd2x2wd}

3) Doma (first blood)

When I loaded it into IDA, it seems very strange. It’s obfuscated, can’t be decompiled, it do a lot of data pushing and modifying and so, and also it enters some kind of a switch case every one has it’s own functionality.

?

?

So I though that the best technique to solve this is through dynamic analysis. For that I loaded it into x64 debugger. And when it asked for my input I set the debugger to stop the process.

?

As you can see in the demo, I started the app, and when it came to the point to ask for my input, I told the debugger to break when I entered the input (it will break when the thread comes to the user land because the app issued a system call and now the thread runs in the kernel), then after I entered my input, the debugger would break on the first user-mode code it faces (which is the code for ntdll in our process address space), and to get to our code in our application I pressed the go to user code to get the caller of this module (ntdll), so now we are in the actual code of the doma app (after it called vfscanf.)

The easy way now is to trace our input, so i put a hardware break point on my input for any read operation done on the first byte of it :

?

As you can see, it hits the break point when it reads the first character of my input. Then it jumps to another location. And guess what, Those bunches of instructions are our key to solving the challenge.

Those instructions take our character, then do a lot of math and logical operations, and finally turn it into 4 bytes (DWORD). then compare it with another stream of data:

?

If the comparison failed, It will jump to another location and return to the switch but this case with case = 4, and when I analyzed this case, it just prints to us the wrong flag :(. But if not, and the string matches; it will do some stack cleaning, then go to the switch where case = 2 and take the other character of my input, then pass it to the same encryption (actually, its more like hashing that encryption; it’s irreversible), with of course checking the other DWORD from the hash buffer it has.

?

So now we have a good understanding of how the app works. I thought that the best way to solve this was through brute- force. So I wrote this C++ code, and instead of trying to rewrite the hashing code into a high-level one, I used inline-assembly (with some simple modifications to the assembly code, like removing the stack cleaning and any x64 specific register to make it run as x86). This is the code:

#include <stdio.h>

bool CheckFlag(int source, int dest)
{
	bool retVar;
	__asm
	{
		mov edx, source
		inc edx
		mov ebx, edx
		shl ebx, 0AH
		xor ebx, edx
		mov ecx, ebx
		shr ecx, 1h
		push ebx
		not ebx
		sub ecx, ebx
		pop ebx
		sub ecx, 1h
		lea edx, [ecx * 8]
		xor edx, ecx
		mov ecx, edx
		shr ecx, 5h
		push edx
		not edx
		sub ecx, edx
		pop edx
		sub ecx, 1
		mov edx, ecx
		shl edx, 4
		xor edx, ecx
		mov ebx, edx
		shr ebx, 11h
		push edx
		not edx
		sub ebx, edx
		pop edx
		sub ebx, 1
		mov edx, ebx
		mov ecx, ebx
		shr ecx, 6
		and edx, 7Fh
		shl edx, 13h
		xor edx, ecx
		mov ecx, ebx
		shl ecx, 19h
		xor ecx, ebx
		push ecx
		not ecx
		sub edx, ecx
		pop ecx
		sub edx, 1

		mov ecx, dest

		cmp edx, ecx
		jne WrongFlag
		mov retVar, 0
		jmp NopInstr

		WrongFlag:
		mov retVar, 1

		NopInstr:
		nop

	}
	return retVar;
}


int main()
{
	char flag[1024];
	bool check = true;
	int counter = 0;
	int arraySize = 48;
	int destData[] = { 0xb99d68d8, 0x8ef8f6c3,0x3194ec2e, 0xb99d68d8, 0x33af2d13, 0x70a549c3, 0x7f69c81e, 0xcfef5b0b, 0x030fe761, 0xdc310a37,
0xcbba9c51, 0xcbba9c51, 0xd659a5a8, 0xcbba9c51, 0xf4c73ebf, 0x930b26e3, 0xb78ac2e7, 0x45e26648, 0x70c1a0e1, 0xea1b9f56, 0xf2475372,
0x030fe761, 0xf4c73ebf, 0xdc310a37, 0x115ea782, 0x70c1a0e1, 0x70c1a0e1, 0x8288d321, 0xf2475372, 0xd659a5a8, 0xdc310a37, 0xb78ac2e7,
0xb78ac2e7, 0xf2475372, 0x9d07d8da, 0x9d07d8da, 0xcbba9c51, 0xb78ac2e7, 0xf2475372, 0x9d07d8da, 0xd659a5a8, 0xdc310a37, 0xdc310a37,
0xcfef5b0b, 0xf4c73ebf, 0x030fe761, 0xcbba9c51, 0xabef6fef };



	while (counter != arraySize)
	{
		for (int i = 32; i < 127; i++)
		{ 
			check = CheckFlag(i, destData[counter]);
			if (!check)
			{
				flag[counter] = char(i);
				break;
			}
		}

		printf("%c\n", flag[counter]);
		counter++;
	}
	flag[counter] = '\0';
	printf("the flag is : %s\n", flag);
		
	return 0;
}

It’s pretty simple code; just do a simple brute-forcing for all ascii-defined characters. and when it matches the same hash value, it will return FALSE, otherwise TRUE. and if true, it will push that value to the flag array.

Don’t forget that the buffer of hashes is shown in memory as a little-indian, so I changed it into a big-indian before storing it in the destData[] array (which is the hash array).

and it worked.

?

Note: compile the code as an x86 app because Windows doesn’t support inline assembly in x64 apps anymore.

flag : EGCERT{6d944e4c857b10dc9abb20e9550334503e996cd4}

AND THAT’S ALL

thanks for reading.

Tags:

Categories:

Updated: