De-obfuscating and reversing a .NET/C# spyware
Tools used: peid, de4dot, strings.exe, dnspy x86, .NET Reflector (trial) ~ inside an isolated Windows 10 VM
Technique: Static binary analysis, reverse engineering/decompiling binary, debugging console output to dump encrypted strings.
File Hash: 8738d53860c8b439cf7f1b672685757b6ff1021b912b3997cbc679b20e210e26
File Name: 3mm thk x 1mtr x 10mtr — Original DWG for production.cab
We received an interesting malware recently. A quick sandbox analysis showed that the malware used network scanning, timeout commands to determine and delay the execution inside a sandbox. So, this was not something that we see regularly at our workplace.
This sparked my curiosity to see how the malware was written, so I posed myself with a challenge to extract same commands I saw in automated analysis statically, right from the binary without executing it. I tried to keep the write-up as short as possible and only included things that worked and not the things I experimented and failed with. This by no means a perfect analysis or write-up and can be done better, being this my first one.
The binary was inside a .CAB archive and extracting it gave me the .EXE, so I opened up PEiD tool to determine the type of the executable. It showed me that the executable was compiled as “Microsoft Visual C# / Basic .NET”.
Figure 1 PEiD output
Now I ran strings.exe to parse out all the ASCII & Unicode strings > 3 characters from the binary. Skimmed through the output and saw some interesting things like some base64 encoded strings, I tried decoding these strings but that gave me gibberish output, so I kept looking through strings and found some URLs and strings related to ‘smartassembly’. Checked this out and got to know that SmartAssembly is an obfuscator mainly used to secure legit .NET code. Strings output:
Figure 2 Strings output
To make sure it was the case, I used de4dot tool to check the obfuscation type of the binary and it returned obfuscation as “SmartAssembly 188.8.131.52”.
Figure 3 de4dot detecting obfuscation type
de4dot can also be used to de-obfuscate binaries that are using some common obfuscators. So, I tried to de-obfuscate the binary and it worked!
Figure 4 de-obfuscating the binary with de4dot
Now, I tried to run strings.exe again on the de-obfuscated binary in hope to find the encrypted base64 strings to be parsed as clear-text strings. But the output showed me the same, encrypted base64 encoded strings as before. So, I tried a tool called ‘.NET Reflector’ to disassemble the .net code of the binary. But this did not get me much far as classes were in assembly language and the ‘decompile’ feature of the tool did not work for some reason.
I looked for other tools and found ‘dnSpy’. This tool is like a .NET/C# IDE *cough VS Code cough* it decompiles the executable and lets you modify the code from functions to methods to classes separately as per your need. It then packs/compiles the assembly and creates an executable with modified code. dnSpy window after loading our malicious executable on it:
Figure 5 dnSpy window
So, as per my initial triage on .NET Reflector, I knew that there’s a class called ‘Class13’ in the root namespace of the assembly which contains all the important methods responsible to decrypt encrypted strings and do other main operations the Spyware is made to do on the system. But I wanted to see the starting point of the program to understand the flow, so I found the entry point under namespace ‘ns0’ and found ‘Class0’. This class contained the ‘Main()’ function which is required to be the initiator of a .NET program. This function also initiates ‘Class13.smethod_22(class2_);’ as shown in the screenshot below.
Figure 6 Class0 entry point
Following the execution flow, checking the code of ‘smethod_22’ reveals details of first stage of foothold on the system. The program checks the environment first, dumps some code in an .exe file in TEMP directory and some encrypted base64 encoded strings are being fed to ‘smethod_29’ as input parameter.
Figure 7 Class13 smethod_22
Moving to ‘smethod_29’ method, it takes string as input parameter and as shown in the screenshot, it uses some default cryptographic functions available in .NET libraries to decrypt the strings and returns the decrypted string. There are references to smethod_29 with strings as input param through Class13 and other classes of the program.
Figure 8 Class13 smethod_29
Now to decrypt all the encrypted strings in the program, I tried to take the code of this method and write a program myself to decrypt the strings, but it did not work out since I was getting building errors. I tried a lot to troubleshoot the errors but could not do it, so I modified the ‘smethod_29’ method and added a line ‘Console.WriteLine(text)’. Here ‘text’ is the variable where the program stores the decrypted string. So, this line will basically write the output of ‘text’ variable on the debug console and hopefully we will get the decrypted strings.
Figure 9 console.writeline() to dump decrypted strings
I compiled the executable and ran it in the dnSpy debugger and voila! As the program ran, it started throwing out the decrypted strings! But some functions of the program did not go successful like generating a notepad.exe in the TEMP folder (is a function of the malware, check threatgrid analysis for full flow) since it was running in the debugger and hence the program exists after a while.
Figure 10 debug output
Still, a lot more information can be extracted by diving further into the code and understanding how the .NET encryption functions work, also if we can recreate the decryption methods and then run a loop to decrypt those strings, we can decrypt all the encrypted strings without debugging the program.