<-- Flare-On 2015 Index / FLARE-On 2015 Challenge #7 
1
FLARE-On 2015 Challenge #7

Date: Aug 19, 2015

CHALLENGE MATERIALS:

filename:    YUSoMeta    DOWNLOAD
md5d17e49a45830a40c844f2bbf1046c99a
size16 k (15,872 bytes)
type.NET 4.0 Console App
Original FLARE AuthorMatt Graeber
 
tool:    CFF Explorer / PE Viewer/Editor    Visit Website
tool:    de4dot / .NET de-obfuscator and unpacker    Visit Website
tool:    ILSpy / .NET decompiler    Visit Website
tool:    Debugging Tools for Windows / Debugger    Visit Website
tool:    SOSEX Debugger Extension / Managed Code debugging helper    Visit Website

SUMMARY:
This looks like the usual command line crackme prompting for a password, but because the application was written using the .NET framework, we must take a different approach to getting it running inside a debugger.

FIRST LAUNCH:
A hex dump of the first few bytes of YUSoMeta indicates it is a PE executable image, so we rename and launch it from the command line. The challenge prompts us for "the correct password" after a warning that the program is "100% tamper-proof". Cute.

Flare-On 2015 Challenge #7 - Running from Command Line

Viewing the executable in CFF Explorer indicates this is a .NET command line executable. This is also evident by the presence of the CLR/.NET data directory in the NT Optional Headers. Had you tried to debug this executable using OllyDbg, you would initially find yourself at the ".NET entry point" breakpoint in the mscoree module. A .NET executable can also be identified in the Depends (Dependency Walker) tool due to the dependency on MSCOREE.DLL.

VIEW THE .NET/CLR RUNTIME INFORMATION:
In CFF Explorer, navigate to the ".NET Directory" -> "MetaData Header" in the left pane. Note the .NET/CLR version string is v4.0.30319. If you don't have CFF Explorer handy, a quick way to find the version string of a .NET framework application is to view the executable in a hex editor and search for the ASCII string "BSJB", the signature of the MetaData header. Once found, look ahead 12 bytes and there's the version string.

Lucky for us, .NET code can usually be decompiled very easily. First, let's glean any additional information we can find from the embedded .NET assembly manifest using Microsoft's own ildasm.exe tool (Intermediate Language Disassembler). Ildasm is available with all versions of Visual Studio and the Microsoft SDKs. When we attempt to load YUSoMeta.exe in ildasm, we get the message "Protected module -- cannot disassemble". This error is a telltale sign that the module has been obfuscated, and its no surprise a Microsoft tool chooses not to cooperate in bypassing any sort of protection scheme.

There is a reason the first incarnation of .NET programming (Visual Studio .NET 2002) bundled a free version of a program named "Dotfuscator". Because of the nature of how .NET code runs on top of a virtual machine (IL assembly is essentially Microsoft's version of Java bytecode), you can achieve a near-perfect reconstruction of the original source code with only the released executable and the right tools. Microsoft knew they had to offer something to help developers adopt the .NET paradigm, while dissuading the casual reverse engineer. One method to prevent prying eyes from the inner workings of a .NET module (without breaking it) is to obfuscate it. "Dotfuscator" does this, and dozens of other more elaborate obfuscation schemes have been developed to tackle the problem over the years. This usually messes up the namespace, class and method names, but can also add code and utilitize other techniques to make the flow of the program more difficult to understand, while retaining the same program functionality.

VIEWING THE ASSEMBLY MANIFEST:
The ILSpy tool has no problems displaying the assembly manifest, even if the assembly has been obfuscated. After we load YUSoMeta.exe in ILSpy, we can see the assembly manifest at the root of the module's tree. The "Powered by SmartAssembly 6.9.0.114" is the indicator of the obfuscation method used. Near the bottom, we find the string "SuppressIldasm". This was probably the flag that caused ildasm to refuse to cooperate.

Flare-On 2015 Challenge #7 - Viewing assembly information

DEOBFUSCATING THE EXECUTABLE:
Luckily, the de4dot tool will deobfuscate the executable so we can analyze it with a decompiler (or anything else). Running the following command will produce a "clean" executable:
    de4dot.exe yusometa.exe
We now have YUSoMeta-cleaned.exe in the same directory, which is about 6k smaller! Load this version in ILSpy and navigate through the module's tree in the left pane. After a bit of trial and error, you'll find there are two source files that appear to have useful source code; Class5.cs and Class3.cs. The namespaces and other identifiers used in the source code are generic, such as "ns1", "string_0", "Class1", but that shouldn't be a problem because we can now see what these identifiers do and how they are used. Class3.cs happens to have the program's entry point at main().

Flare-On 2015 Challenge #7 - Decompiler output for Class3

After main() declares some byte arrays, we see the following code beginning at line #355. NOTE: You can turn on line numbers in the program options as well as save off copies of the source files, should you want to analyze them in your own viewer/editor.

Console.WriteLine(Encoding.ASCII.GetString(bytes)); Console.Write(Encoding.ASCII.GetString(bytes2)); string text = Console.ReadLine().Trim(); string b = Class3.smethod_0(class1_, byte_2) + '_' + Class3.smethod_3(); if (text == b) { Console.WriteLine(Encoding.ASCII.GetString(bytes4)); Console.Write(Encoding.ASCII.GetString(bytes5)); Console.WriteLine(Class3.smethod_1(text, byte_)); return; } Console.WriteLine(Encoding.ASCII.GetString(bytes3));

Right away, we can see those byte arrays we passed were just cryptic ways of declaring the application's "welcome" strings along with some seemingly unused arrays thrown in the mix. Console.ReadLine().Trim() reads the password from the user, with the surrounding whitespace removed. The next line calls two functions to piece together two strings separated by an underscore "_", assigning the result to variable "b". Because the contents of "b" are compared on the next line against the user input, "b" is probably the password string "expected" by the program.

This is a boon for us, because we can just read the value of variable "b" in a debugger without spending time investigating the calls to the two mystery functions (or other parts of the algorithm). The only thing we need to figure out is how to get to this spot within the debugger. The Class3 method symbols are tricky to set breakpoints on because the names were obfuscated, but the System methods are easy. Setting a breakpoint on the first Console.Write() should get us to the right spot in main() fairly quickly, followed by a subsequent break on String.Trim() to get us adjacent to the code we are interested in. We'll step through the rest of the instructions, watching the contents of the registers until we see an arbitrary string in one register and our user input in another. We can guess our user input won't be loaded until just prior to the comparison statement "if (text == b)". We'll then know that the "b" variable has been fully built.

HOOKING UP A DEBUGGER:
Since we'll be debugging managed code, we're going to use the WinDbg family of debuggers with the SOS and SOSEX managed extensions. We primarily need these extensions to set managed breakpoints. You should find SOS.DLL in the .NET Framework 4.0 system directory as "%WINDIR%\Microsoft.NET\Framework\v4.0.30319\SOS.dll". Copy this file to the to where you installed the Microsoft Debugging Tools (i.e. windbg.exe, cdb.exe). Also copy into this directory the latest SOSEX.DLL from the link at the top of this page. From the command prompt, launch the debugger as follows:
cdb YUSoMeta.exe
NOTE: If you prefer, you can use the GUI version of the debugger by replacing cdb.exe with windbg.exe.

The following debugger commands will load the managed extensions and configure our breakpoints:
.load sos
.load sosex
!mbm System.Console.Write
g
!mbm System.String.Trim
g
The first "g" command will run the program to the first Console.Write(). You'll see the 1st line of the "welcome" message from the prior Console.WriteLine() call. The next !mbm command allows us to break on the next String.Trim() call. The 2nd "g" command runs the program again, but we'll be prompted for the password before the breakpoint on String.Trim() is hit. Type in anything that you will recognize as "your" string when stepping through the debugger. I used "HOWDY".

Flare-On 2015 Challenge #7 - Tracing User Input String

FINDING THE PASSWORD:
Now issue the following two-command combination separated by a semicolon:
p;!mdso
We're using the assembly step-over command ("p"), since there is no managed equivalent (that I was aware of anyway), so its going to take many more assembly instructions to step over this single line of managed code. The !mdso dumps CPU registers and managed object references on the stack, so we can see the state of the local variables after each instruction executes. Press the <UP> key to bring up another copy of our command string "p;!mdso" and press <ENTER>. Continue doing this until you see your user input string appear in one of the CPU registers. After about 6 instructions, you'll begin to see the "password" string taking shape. After about 30, you should see your user input appear in one of the registers. Don't step any further. Look in the local variable dump output for a plaintext string that looks like it might be the password.

Flare-On 2015 Challenge #7 - Tracing Expected Password String in Debugger

In the screenshot above, we know its not the string in EBX because this string doesn't contain the underscore "_". The string in EBX is a temporary result of one of the "halves" of the final password string. The only other string is the one in ESI that contains "metaprogrammingisherd_DD9BE1704C690FB422F1509A46ABC988". This string certainly isn't a flare-on.com e-mail address, but we'll try plugging it in anyway. Issue the "q" command to exit the debugger and rerun YUSoMeta.exe without the debugger.

TRYING THE PASSWORD:
Plugging in the password we found confirmed we got it right. The program proceeded to decrypt the final solution, which was indeed a flare-on.com e-mail address. It was nice we could take advantage of a shortcut, thanks to the ability to view the deobfuscated code!

Flare-On 2015 Challenge #7 - Let Program Decrypt the Solution for Us

CONCLUSION:
Two anomalies I noticed while doing this challenge were that the managed breakpoints didn't seem to work on the deobfuscated version of the program once the user input string was entered. Also, if you try to enter the correct password in the deobfuscated version, you'll find it is not recognized (i.e. you get the generic "Y U tamper with me?" message).

The official solution used the dnSpy tool as the .NET decompiler, but that wasn't the only difference. The author digs in to how you can invoke some of the decryption methods in the program from a PowerShell script to piece together the two halves of the password, something I had no idea was possible! An alternative method is then offered for finding the password as a memory artifact once the program terminates. This is similar to my method, but relies on a neat debugger script rather than stepping through the code. Finally, it was explained why the deobfuscated version failed to decrypt the password. The password logic was based on attributes from the obfuscated version of the executable!

E-MAIL RESPONSE:
Response from Justr3adth3sourc3@flare-on.com:
Subject: FLARE-On Challenge #7 Completed! From: Justr3adth3sourc3@flare-on.com To: <HIDDEN> Date: Wed, 19 Aug 2015 00:00:03 -0400 That challenge was supposed to stop you. I suppose we underestimated you, Contestant #743. We didn't want to, but we're going to have to start bringing out our hard ones. The password to the attached zip archive is "flare". I hope your good fortune continues. -FLARE attachment_filename="FE9D3BA1789DC6371105042D80291205.zip"

<< Flare-On 2015 Index  --  Go on to Challenge #8 >>

 1:1