Some time ago I solved the Airplane challenge published by Israeli Shin-Bet (Shabak). The crackme has three levels of increasing difficulty. Each one is a 32 bit Windows application. It was a very pleasant task, not difficult but also not too trivial. In this writeup I will present my solutions.
Task 1 and 2 have been described in the previous part, you can read it here. Now it’s time for the final one!
Task 3
http://10100110110100001100001011000100110000101101011.com/Airplane/3_with_the_best.php
Mirror [task3], password “Challenge”
This time the crackme comes with a hint:
hint: Maybe this program doesn't do more than it seems, our special agent have told us that when the program was executed in a different country, it behaved differently
It reminds me of the techniques used by some malware to target only the chosen countries. Usually it is implemented in one of the two ways:
- sending a request to some of the services that gives geolocation data basing on the external IP
- checking the installed language/keyboard layout
Let’s run the crackme and observe how it behaves, if it makes any internet connections etc. (we can use i.e. ProcMon).
The crackme printed a message: “May you enter Deep and Dreamless Slumber”:
…and terminated after some timeout. No internet connection has been made. So, I guess it will do something about checking the installed language.
This time I will start from the static analysis in IDA. Let’s load the application and have a look at the referenced functions:
There is GetLocaleInfoEx. I suspect it will be involved in verification process, so, let’s follow where it is called:
The output is saved in a variable of WORD size. If I try to follow this variable and check the references, I don’t find anything more than the above line:
However, it’s upper byte seems to be referenced somewhere else!
It seems if this flag matches, some other function is copied on the place of the function printing the initial “slumber” message:
So, we have some self-modifying code here 🙂 ! Let’s see what is this function doing:
fs:30h -> PEB PEB + 0XC -> _PEB_LDR_DATA Ldr Ldr + 0x14 -> _LIST_ENTRY InMemoryOrderModuleList
It doesn’t seem to be a function printing the password. Instead, it searches Kernel32.dll through the loaded DLLs:
We can also see an atypical NOP instruction, that can confuse some debuggers:
OllyDbg and it’s derivatives fails to parse it properly:
If we want to analyze it under OllyDbg we need to substitute this fragment by a typical NOP (0x90) in order to get a clear view:
Now I will do some dynamic analysis in OllyDbg. I set the breakpoint on the flag check (the one that was deciding whether or not to overwrite the function):
…and when it was hit, I changed of the Z flag in the registry. After the function was overwritten, I enforce OllyDbg to re-analyze the code and then set the breakpoint at the function’s beginning:
When the breakpoint is hit, we can step follow the function’s execution to see is details what it is doing.
At the end there is something interesting – a new PE file in the memory:
We can see that the previously stored pointer to kernel32.dll is being overwritten by the pointer to this module:
I dumped this PE and unpapped it using pe_unmapper in order to get a better view. It is named stub.dll and it exports one function: GetComputerNameW:
After following references in IDA, we can find, that this module was unpacked just before the flag check:
It was manually loaded in the memory (without being dropped on the disk and without using LoadLibrary function). This trick is also very often used in malware.
Anyways, now we need to find out where the stub.dll is used. So, I set the breakpoint on this module:
The breakpoint is hit inside ntdll:
Now I set the breakpoint on the .text section of the main module (Third.exe) to see the point where the execution returns:
This is where the stub.dll was referenced from inside the main module:
So, at this point the application gets the address of the function: “GetComputerNameW”. It can fetch this function either from kernel32.dll (if the locale flag was not set) or from the stub.dll (if the locale flag was set).
It seems we are pretty close to the solution, because some formatted printing (“%s”) is done just after that lines (probably this is the flag being printed). Most probably the key lies inside the function GetComputerName, so let’s go there.
Inside GetComputerNameW:
Again OllyDbg cannot manage parsing some instructions. So, I opened the dumped version of Stub.dll in IDA and used as a reference.
This is how the beginning of the function looks:
fs:30h -> PEB PEB + 0x10 -> _RTL_USER_PROCESS_PARAMETERS ProcessParameters ProcessParameters + 0x44 -> _UNICODE_STRING CommandLine.Buffer
The function fetches the command line of the main process, and then process the buffer.
Again, the atypical NOP instructions has been used (marked red on the picture):
We can see two buffers being compared. One of them is the command line buffer stored the memory, and another is hardcoded in the stub.dll. Four consecutive DWORDs are compared. If those two buffers are not matching, then the function sets an error code and exits:
The hardcoded buffer starts at RVA 0x2000 – that is the beginning of .rdata section).
Of course we need the above function to exit without error – then our flag will be printed.
It is easy to conclude, that in order to get the flag, we must have the same values in the memory buffer as in the hardocoded buffer.
I set the breakpoint before this comparison started. The, I just copied the hardcoded buffer and overwritten by its content the buffer in the memory:
Now, let it run. And this is what we get:
Solved!
We reached the “Airplane Complete” page.
http://10100110110100001100001011000100110000101101011.com/Airplane/Airplane_Complete_U_D_1.html
R. Sanchez is safe, happy end! 🙂
Pingback: Week 26 – 2017 – This Week In 4n6