The-FLARE-On-Challenge-2015/Challenge-7
You are here | Challenge 7
|
Files
Uncompress 0CC92381BDCA47754B144A4FC2E41623.zip (password is "flare") and you will get a file named YUSoMeta with following properties:
MD5 | d17e49a45830a40c844f2bbf1046c99a |
---|---|
SHA1 | eb21f7a88559f9ef761df37fff0bb617028b3562 |
File type | PE32 executable (console) Intel 80386 Mono/.Net assembly, for MS Windows |
Analysis
Deobfuscation
We have to deal with a .Net executable. Let's use dnSpy to analyze it. We immediately see that the executable is obfuscated using SmartAssembly.
"SmartAssembly is an obfuscator that helps protect your application against reverse-engineering or modification, by making it difficult for a third-party to access your source code." (Source: https://www.red-gate.com/products/dotnet-development/smartassembly/)
Moreover, if we go to the entry point, we have another indication that the executable is obfuscated:
Let's use a second tool, De4dot to deobfuscate it:
C:\_malware\de4dot>de4dot.exe ..\YUSoMeta.exe de4dot v3.1.41592.3405 Copyright (C) 2011-2014 [email protected] Latest version and source code: https://github.com/0xd4d/de4dot 21 deobfuscator modules loaded! Detected SmartAssembly 6.9.0.114 (C:\_malware\YUSoMeta.exe) Cleaning C:\_malware\YUSoMeta.exe Renaming all obfuscated symbols Saving C:\_malware\YUSoMeta-cleaned.exe
Now, open the cleaned file (YUSoMeta-cleaned.exe) in dnSpy. It should be much better:
Password test
Just after some array definitions, here is the code we can see:
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));
- In line 286, the user input is saved to a variable named text.
- In line 287, there is a string b (expected password) built from the concatenation of 2 substrings, linked with the "_" character
- In line 288, there is a test that checks whether both variables are equal. As depicted below, we can easily check that if this test succeeds, it will mean we have provided the expected password:
>>> bytes4 = [84, 104, 97, 110, 107, 32, 121, 111, 117, 32, ... 102, 111, 114, 32, 112, 114, 111, 118, 105, 100, ... 105, 110, 103, 32, 116, 104, 101, 32, 99, 111, ... 114, 114, 101, 99, 116, 32, 112, 97, 115, 115, ... 119, 111, 114, 100, 46] >>> ''.join([chr(i) for i in bytes4]) 'Thank you for providing the correct password.'
Decode password
First part
Let's decode the first part of the password (the one before the "_" character). We see that it calls Class3.smethod_0 and sends byte_2 as parameter.
Class3.smethod_0(class1_, byte_2)
Scrolling some lines above shows that byte_2 is an array defined as follows:
byte[] byte_2 = new byte[] {31,100,116,97,0,84,69,21,115,97,109,29,79,68,21,104,115,104,21,84,78};
Let's click on smethod_0 to jump to the function:
static string smethod_0(Class1 class1_0, byte[] byte_0)
{
byte[] array = Class3.smethod_2();
string text = "";
for (int i = 0; i < byte_0.Length; i++)
{
text += (char)(byte_0[i] ^ array[i % array.Length]);
}
return text;
}
This function is simply XOR'ing each item of the array sent as parameter with a rolling array defined by Class3.smethod_2(). Let's click on smethod_2() to reveal the following code:
static byte[] smethod_2()
{
return Assembly.GetExecutingAssembly().ManifestModule.ResolveMethod(100663297).GetMethodBody().GetILAsByteArray();
}
This code actually gets the assembly content of the method returned by the token number 100663297 (0x06000001 in hex).
+-------------+------------+ | Token (int) | 100663297 | +-------------+------------+ | Token (hex) | 0x06 | <--- index for a MetaData table (0x06 = MethodDef) | | 000001 | <--- index of the item in that table (1 = .cctor) +-------------+------------+
CFF Explorer has the ability to disassemble the instructions of our item:
Now that we know that smethod_2 is returning [0x72, 0x01, 0x00, 0x00, 0x70, 0x26, 0x2A], we can determine the 1st part of the password with a small python script:
#!/usr/bin/env python
array = [0x72, 0x01, 0x00, 0x00, 0x70, 0x26, 0x2A]
byte_0 = [31,100,116,97,0,84,69,21,115,97,109,29,79,68,21,104,115,104,21,84,78]
print ''.join(chr(i ^ array[c % len(array)]) for (c, i) in enumerate(byte_0))
It returns:
metaprogrammingisherd
Second part
The second part of the password is the return value of Class3.smethod_3():
static string smethod_3()
{
StringBuilder stringBuilder = new StringBuilder();
MD5 mD = MD5.Create();
foreach (CustomAttributeData current in CustomAttributeData.GetCustomAttributes(Assembly.GetExecutingAssembly()))
{
stringBuilder.Append(current.ToString());
}
byte[] bytes = Encoding.Unicode.GetBytes(stringBuilder.ToString());
byte[] value = mD.ComputeHash(bytes);
return BitConverter.ToString(value).Replace("-", "");
}
This function is actually concatenating strings from the custom attributes (see below screenshot) and computing the MD5 sum of the resulting string.
The problem is that we don't know in what order these attributes are concatenated. This is why we will use a different approach and will take advantage of the fact that we already know the 1st part of the password to look for strings in memory.
Let's open the executable in WinDbg and simply run till it exits. Once done, let's import the very useful sos.dll library which will help us dump the strings, as decribed here.
In WinDbg, enter the following commands:
> .load sos > .foreach (obj {!dumpheap -type System.String -short}) {.printf "%mu\n",${obj}+C}
This last command should dump all strings and we should be able to find the complete password:
Solution
Once the good password is provided to the application, it shows the solution:
[email protected]
Comments
Keywords: reverse-engineering challenge flare fireeye dotnet obfuscation meta