During a long-term investigation, Brian Wallace discovered two forensic artefacts - both GUIDs - which can be used to determine whether multiple malware samples are from the same Visual Studio project, effectively identifying the family, and to identify samples that are the result of the same build, allowing for the identification of post-compilation modifications made by tools such as builders. Here, he describes his discoveries and how these new artefacts can help malware hunters around the world.

During a long-term investigation, I uncovered forensic artefacts not commonly used in .NET assembly analysis. These artefacts are both GUIDs (globally unique identifiers). One is created by Visual Studio on the creation of new projects and stored as a string, the other is generated on every build and stored as a binary value. These GUIDs can be used to determine whether multiple samples are from the same Visual Studio project, effectively identifying the family, and to identify samples that are the result of the same build, allowing for the identification of post-compilation modifications made by tools such as builders. After releasing an open-source tool to extract these GUIDs, we suggested that VirusTotal integrate this functionality. They have done so, allowing for these new artefacts to help malware hunters around the world.

.NET GUIDS

Months deep into an investigation of an Iranian operation we dubbed ‘Operation Cleaver’ [1], I found myself buried up to my eyes in malware samples to reverse engineer. As an employee of a start-up, I am no stranger to situations where the options are innovate or die, and this was one of them. Through some simple pre-processing data I gathered on the large number of samples located on a public FTP server, I could tell that a substantial percentage of the malware had been developed in a .NET-based language. Having previously worked as a software engineer developing in C#, I was quite familiar with .NET and recalled that it makes greater use of GUIDs than some other languages. I identified two GUIDs that could be of assistance in reducing the number of samples that needed to be fully reversed.

MVID The Module Version ID, or MVID, is a GUID that can be used to distinguish various versions of a .NET module. This value is generated at build time, resulting in a new GUID for each unique build. This GUID is stored in a binary format (as opposed to string format) in a defined location in .NET assemblies. The GUID resides in the GUID heap, and its location is identified in the Module table of the MetaData tables in the .NET MetaData header. What can we do with knowledge of this identifier? By itself, it can assist in identifying post-compilation modifications made by tools such as builders that generate customized malware binaries from a common source binary. In other cases, it can assist in identifying instances where a legitimate .NET sample has been modified. It should be noted that the MVID can be modified by a malware operator. An informed malware operator could do this with a hex editor. Extraction methods Extracting the MVID statically by parsing the PE and the .NET metadata can be somewhat difficult. This is because the format relies on a large number of variables that can lead to intricacies in parsers. There are a number of simpler ways to extract the MVID. Extracting with ildasm The disassembling counterpart to ilasm (Microsoft’s tool for converting Common Intermediate Language code to a portable executable), ildasm, is used to convert .NET assemblies back to the common intermediate language code. In addition, it extracts and displays the MVID which can be searched for specifically with the find command on Windows platforms. > ildasm /text /all af7ce8dcb16b6344de9856e96ce83b642bcc5ee1c0d29e3a0dcd96e67c65a085 | find "MVID" // MVID: {F2A0DA69-155E-4543-AD0A-206026E206DB} Extracting with monodis As a heavy Linux user, it would be remiss of me not to include a more Linux-friendly solution as well. We can obtain similar results using monodis, ildasm’s counterpart in Mono (an open source implementation of Microsoft’s .NET framework), along with grep, the Linux equivalent to Microsoft's find. > monodis af7ce8dcb16b6344de9856e96ce83b642bcc5ee1c0d29e3a0dcd96e67c65a085 | grep GUID .module E.exe // GUID = {F2A0DA69-155E-4543-AD0A-206026E206DB} Extract with .NET (insecure) It should come as no surprise to developers with .NET experience that there is a simple method to obtain the MVID of another .NET assembly: var aAssemblyToAnalyze = Assembly.LoadFile(strPathToFileToAnalyse); // DO NOT DO THIS var mvid = aAssemblyToAnalyze.ManifestModule.ModuleVersionID; This method should not be used, and the next section will explain why. Extract with .NET (likely secure) While working on a method to extract the MVID as well as the TypeLib ID from .NET samples, I became curious about the additional attack surface created by loading a .NET assembly with no intention of executing it. I found something interesting when testing with mixed .NET assemblies. Mixed assemblies, which are built with both .NET and unmanaged code, were intended to act as a stepping stone for developers migrating projects from purely unmanaged code to .NET. Mixed assemblies are still identified as .NET assemblies, but also contain a DllMain entry point, which is executed when loaded with Assembly.Load and related functions. A full description of this issue can be found in [2]. The brief explanation is that it allows for an assembly to execute code when loaded with Assembly.LoadFile and similar methods, which could be abused to gain control of a system inspecting a malicious sample. In order to work around this issue, any assemblies loaded purely for inspection should be loaded with Assembly.ReflectionOnlyLoadFrom instead of Assembly.LoadFile, or any of the reflection-only method alternatives. While it does protect from this specific issue, there may be other unexplored attack surfaces, so it’s best, in my opinion, to treat this method as risky but more secure than the non-reflection-only method. var aAssemblyToAnalyze = Assembly.ReflectionOnlyLoadFrom(strPathToFileToAnalyse); var mvid = aAssemblyToAnalyze.ManifestModule.ModuleVersionID;