From aldeid
Jump to navigation Jump to search
You are here
Challenge 7


Uncompress (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



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."


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:
21 deobfuscator modules loaded!

Detected SmartAssembly (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:

284 Console.WriteLine(Encoding.ASCII.GetString(bytes));
285 Console.Write(Encoding.ASCII.GetString(bytes2));
286 string text = Console.ReadLine().Trim();
287 string b = Class3.smethod_0(class1_, byte_2) + '_' + Class3.smethod_3();
288 if (text == b)
289 {
290     Console.WriteLine(Encoding.ASCII.GetString(bytes4));
291     Console.Write(Encoding.ASCII.GetString(bytes5));
292     Console.WriteLine(Class3.smethod_1(text, byte_));
293     return;
294 }
295 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:

299 static string smethod_0(Class1 class1_0, byte[] byte_0)
300 {
301     byte[] array = Class3.smethod_2();
302     string text = "";
303     for (int i = 0; i < byte_0.Length; i++)
304     {
305         text += (char)(byte_0[i] ^ array[i % array.Length]);
306     }
307     return text;
308 }

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:

369 static byte[] smethod_2()
370 {
371     return Assembly.GetExecutingAssembly().ManifestModule.ResolveMethod(100663297).GetMethodBody().GetILAsByteArray();
372 }

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:


Second part

The second part of the password is the return value of Class3.smethod_3():

375 static string smethod_3()
376 {
377 	StringBuilder stringBuilder = new StringBuilder();
378 	MD5 mD = MD5.Create();
379 	foreach (CustomAttributeData current in CustomAttributeData.GetCustomAttributes(Assembly.GetExecutingAssembly()))
380 	{
381 		stringBuilder.Append(current.ToString());
382 	}
383 	byte[] bytes = Encoding.Unicode.GetBytes(stringBuilder.ToString());
384 	byte[] value = mD.ComputeHash(bytes);
385 	return BitConverter.ToString(value).Replace("-", "");
386 }

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:



Once the good password is provided to the application, it shows the solution:

[email protected]



blog comments powered by Disqus

Keywords: reverse-engineering challenge flare fireeye dotnet obfuscation meta