ICMTC-2 CTF - Reverse
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.