This summer FireEye’s FLARE team hosted its second annual Flare-On Challenge targeting reverse engineers, malware analysts, and security professionals. In total, there were eleven challenges, each using different anti-reversing techniques and each in different formats. For example, challenges ranged from simple password crack-mes to kernel drivers to stego in images.
This blogpost will highlight four of the eleven challenges (specifically 6, 7, 9, and 11) that we found most interesting as well as some of the more useful tools and materials that would help for future challenges like these.
- Summary: Challenge Six was an obfuscated Android App crack-me which took and verified your input
- Techniques Used: Remote Android debugging, IDAPython
The novelty of this level was that it wasn’t a Windows binary (the majority of the challenges targeted the Windows platform; clearly looking for some Windows reversers ;] ) and it required knowledge of ARM reversing.
At the heart of this level was the ARM shared object library that contained the algorithm for checking the key. Launching the app on either your spare Android malware designated phone or emulator, we see this screen:
Taking a stab at gambling, we try entering “password”. No luck.
Opening it in IDA (if you did this first without running it… you’re in good company) we see that the important part of the library is the compare.
Tracing this compare backwards we find the function which generates the expected input value. All we need to do is statically reverse this. The main part of this decryption function is the factorization of the encrypted password stored in the binary.
The logic from this function can be ported into Python along with the encrypted string. Using IDAPython to extract the necessary data from the binary makes this process a lot easier. For those who have never used IDAPython, the script is included below.
The above logic was exfiltrated from the obfuscated binary through static reversing. IDAPython helped with carving out the right data segments from the app.
Running the final Python script to decrypt the string prints the intended password.
Aside from reversing statically, remote debugging can also be done with gdbserver.py to either attach to the app running on a phone or attach to an emulated android server.
A breakpoint can be then set at the compare and the decrypted flag read out of the debugger. To do this, extract android apk, setup android debugging environment and break at the calls to the shared, obfuscated object.
There are a few good resources online which show how to setup a remote gdb environment on android. Specifically, a few useful resources can be found at the bottom of this post.
Challenge Seven: YUSoMeta
- Summary: Challenge 7 was an obfuscated .NET application that verified a user-supplied password.
- Techniques Used: .NET deobfuscation, Windbg special breakpoints
Challenge 7, YUSoMeta, was a .NET Portable Executable format application. Like every good reverser, we load the .NET application into IDA Pro.
Glancing at the Functions window reveals quite a few peculiarly named methods. Many of the classes and class field names do not consist of exclusively ASCII characters (as exhibited by “normal” .NET applications). This suggests the presence of obfuscation.
Opening the application in a hex editor (our particular choice is HxD), we find an interesting string: “Powered by SmartAssembly 184.108.40.206”.
SmartAssembly is an obfuscator (much like Trail of Bits’ MAST) for .NET applications. Luckily, de4dot is a tool to deobfuscate SmartAssembly protected applications. Deobfuscated, tools such as .NET Reflector can decompile the Common Intermediate Language (CIL) assembly back into C#. Using this, we find a password verification function.
The challenge captures the password obtained by user input and compares it to the expected password as generated by a series of somewhat complex operations. The easiest way to obtain the expected password is to use Windbg.
First, we setup Windbg by loading the SOS debugging extensions to introspect managed programs (aka .NET applications).
Second, we need to set up the symbols path to obtain debugging symbols.
Afterwards, we set a breakpoint on the string equality comparison function, System.String.op_Equality in mscorlib.dll. Note: we run the !name2ee twice because !name2ee always fails on the first issuance.
Upon breaking, we examine the stack objects using !dumpstackobjects. The password used to extract the key should be on the .NET stack.
Challenge Nine: you_are_very_good_at_this
- Summary: Challenge 9 was an obfuscated command line password checking application
- Techniques Used: Intel PIN, Windbg, Python, IDA Pro
Challenge 9, you_are_very_good_at_this, was a x86 Portable Executable command line application that took an argument and verified it against the expected password – a basic crack-me.
Like all good reversers, we immediately open the application in IDA Pro, which, revealed an enormous wall of obfuscated code – clearly dynamic analysis is the way to go.
To us, there are two clear ways of solving this challenge. The first uses pintool, the second, Windbg.
First Solution: Pin
We know that the crack-me is checking the command line input somehow, character by character, through mass amounts of operations. Luckily for us, we don’t really need to know more than that.
Using a simple instruction count Pintool (inscount0.cpp from Pin’s tutorial works perfectly) , we can count the instructions executed to check our input and determine whether it failed on the 1st character or failed on the nth character. This allows us to, byte by byte, brute force the password.
It is apparent that there are more instructions executed in the second case, where the nth character is incorrect and exit() isn’t called until later in the execution of the program. We can use this knowledge to determine that the first n-1 characters are correct inputs.
Using Python, we script the pintool to give us the instruction count of the binary’s execution using every possible printable character for the first character of the password.
Doing this, all inputs give us the same instruction count result except for the input containing the correct first character. This is because the correct character is the only one which passes the application’s validator. When this happens, the binary executes additional instructions that aren’t otherwise run.
Now we know one character, we add a for loop to our script to check for an outlier, and do the same thing for every character of the password… successfully leaking the password!
Last year, Flare-on Challenge 6 was also solvable in this exact way, thanks @gaasedelen for his detailed writeup on this.
Second Solution: Windbg/Python/IDA Pro
To solve this challenge the old fashioned way, we launch WinDbg and set a breakpoint on kernel32!ReadFile. We trace the kernel32!ReadFile caller and manually deobfuscate the password checking loop by cross-analyzing in IDA Pro.
The password checking loop uses a CMPXCHG instruction to compare the characters of the user supplied password and the expected password.
We determined the registers of interest are the AL and BL registers. Tracing the dataflow for the registers of interest reveals that the AL register encounters some transformations, as a function of CL and AH, but ultimately derives from the user supplied buffer. This implies that the BL register contains the transformed character of the expected password.
Fortunately, we are able to precisely breakpoint at an instruction in the password verification loop and extract the necessary register values (namely the BL, CL, and AH registers) to decode the actual password.
To decode the expected password, we take the printed BL, CL, and AH register values for each “character round” and implemented a Python function to reverse the XOR/ROL transformation done on AL.
We unearth the key by joining the output of ror_xor for each “character round”.
Challenge Eleven: CryptoGraph
- Summary: Challenge Eleven was an encrypted jpeg image using the rc5 algorithm and an obfuscated key. It turned out to be a solid ‘reversing’ challenge.
- Techniques Used: RC5 Crypto Algorithm, Windbg, Resource segment carving
The final challenge. Challenge Eleven, CryptoGraph.exe, was a command line binary which, when no arguments were passed, created a junk jpeg file on the system. Looking closer, we see that the binary does accept one command line argument. However, when any number is passed, the binary loops ‘forever’.
Opening the binary in IDA Pro, we assume that the flag will somehow appear in a properly created image. This means we start reversing by tracing up the calls from “WriteFile.”
A few functions up we realize that the resource #124 is being loaded, decrypted and saved as this image file.
The decrypted algorithm is easily identifiable as RC5 through Google. The first result is the Kaspersky report on the Equation Group and their use of RC5/6.
Now all we need is the RC5 decryption key. Unlucky for us, the key is of length 16 bytes and cannot be easily brute forced. However, reversing further, we realize that the key is the result of two distinct RC5 decryption stages.
The first decryption is indexed into using a random index byte between 0x0 and 0xf. This creates an 8 byte key.
This key is then used in another RC5 decryption which actually takes the encrypted source (Resource_122) at an offset, a number which is the function of the same random index byte. This second stage, decrypts only 16 bytes, the 16 byte RC5 key needed for the encrypted jpeg, Resource_124.
Breaking in Windbg, we realize that the decryption of Resource_121 is what is causing the program to seemingly loop forever. In fact, the loops, which run from 0x0 to 0x20, are getting exponentially longer to execute each iteration.
Given the RC5 key length and the algorithm used for indexing into the decrypted Resource_121 which gives us this RC5 key, we determine that only one section of the resource is necessary.
Decrypting only the relevant bits of Resource_121 reduces the execution time significantly.
The indexing algorithm, which is not entirely deterministic, can, at max, index into the first 784 bytes of the decrypted resource.
Because each loop decrypts 48 bytes (hardcoded as an argument passed to the decrypt function), we need to let the main decryption loop run past 0x10 iterations before breaking out of the function.
Using Windbg, we break at the loops to stop after the 0x10th iteration. This means only parts of the key Resource_121 will be decrypted, but thankfully, that’s the only part the 8 byte key needs.
One last thing that needs to be brute forced isa single byte value between 0x0 and 0xf which affects the indexing algorithm. This byte affects the generation of the previously discussed 8 byte key as well as the index into Resource_122 from which a 16 byte key is decrypted.
Scripting this in Windbg (full script found here), we let the binary run 0xf times; each time stopping the loops after 0x10 iterations.
On the 0x9 iteration (the magic indexing byte), the correctly decrypted image is saved to a file, and the flag can be read out :].
Thanks to the FLARE team at FireEye for putting these challenges and successfully forcing me to crack open my Windows VM and learn some new reversing tools. Hope next year’s are just as fun, obscure, and maybe a little harder.
References, Guides, & Tools
Things that we found useful for the challenges.