Normal view

Received — 11 January 2026 Blaze's Security Blog

Analyse, hunt and classify malware using .NET metadata

By: Bart
25 March 2024 at 20:13

Introduction

Earlier last week, I ran into a sample that turned out to be PureCrypter, a loader and obfuscator for all different kinds of malware such as Agent Tesla and RedLine. 

Upon further investigation, I developed Yara rules for the various stages, which can be found here (excluding the final payload):

With that out of the way, all of this reminded me of the fact that we can also write Yara rules for unique identifiers specific to malware written in .NET, or any other .NET assemblies for that matter.

A bit of history

This isn’t my first encounter with analysing .NET malware at scale: several years ago, I co-authored a presentation with Santiago on hunting SteamStealer malware, which was surging exponentially at the time (the malware intended to steal your Steam inventory items and/or your account). A huge thanks goes to Brian Wallace who had developed a tool at the time called GetNetGUIDs which made it trivial to extract all the GUID types and start clustering to identify patterns: basically, which of the malware samples are likely authored by the same person or belong to the same attack campaign.

.NET assemblies or binaries often contain all sorts of metadata, such as the internal assembly name and GUIDs, specifically; the MVID and TYPELIB.

  • GUID: Also known as the TYPELIB ID, generated when creating a new project.

  • MVID: Module Version ID, a unique identifier for a .NET module, generated at build time.

  • TYPELIB: the TYBELIB version – or number of the type library (think major & minor version).

These specific identifiers can be parsed with the strings command and a simple regular expression (regex): [a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}

Taking a sample of PureLogStealer posted by James_in_the_box, you could then write a Yara rule based on the MVID or Typelib detected.

As shown on VirusTotal for this sample:

A screen shot of a computer

Description automatically generated
Figure 1 - Sample with MVID 9066ee39-87f9-4468-9d70-b57c25f29a67

And the resulting (simple) Yara rule, could then be as follows:

rule PureLogStealer_GUID

{

strings:

$mvid = "9066ee39-87f9-4468-9d70-b57c25f29a67" ascii wide fullword

condition:

$mvid

}

There are however some issues with this: 

  • The MVID is stored as a binary value rather than a string, whereas the Typelib GUID is effectively stored as a string and since we only have the MVID here, the sample above will not be detected with this rule.

  • It is important to note that VirusTotal does not seem to report the Typelib.

  • It is cumbersome to “do it the manual way” with strings and regex, especially on larger data sets – and it’s prone to issues such as:

    • false positives: if you run "strings" on the sample and then use the following CyberChef recipe – we get plenty of GUIDs, but only 1 is the actual Typelib;

    • false negatives: we miss out on unique identifiers, which means we might miss detection of samples, campaigns or actors.

Note that with tools such as IlSpy or dnSpy(Ex), you can also view the Typelib GUID and MVID, however, not all tools display all data, for example:

A screenshot of a computer program

Description automatically generated
Figure 2 - dnSpy detects the Typelib GUID of the sample

And if we go the "oldschool" route using ildasm:

Figure 3 - ildasm displays the MVID or Module Version ID


For all the above reasons, let’s go beyond and do more: both with Yara, and with a new Python tool I’ve created.

The now and the tooling

Before we dive into the tooling, some final history to say that Yara has evolved and thanks to that, we can now hunt and detect more effectively due to the following modules added:

  • 2017: introduction of .NET module (link)

  • 2022: introduction of console module (link)

This means that using the .NET module, we can now write a Yara rule like so instead:

import "dotnet"

rule PureLogStealer_GUID

{

condition:

dotnet.guids[0]== "9066ee39-87f9-4468-9d70-b57c25f29a67"

}

And indeed:

Figure 4 - Yara now detects the sample

Yara rule

Let’s now leverage the power of Yara and its dotnet and console modules to write a new Yara rule that displays useful data of any given .NET sample that can be leveraged to create meaningful rules, for example: assembly name, typelib and MVID. 

A screenshot of a computer code

Description automatically generated
Figure 5 - Yara rule to display .NET information to the console

We first verify if the binary is a .NET compiled file, if so, log certain Portable Executable (PE) or binary information to the console as well, and then display all relevant .NET information.

And the output will be, again for the same sample:

A computer screen shot of a computer program

Description automatically generated
Figure 6 - Yara rule output: sample metadata!


Meaning we can now write a rule as follows:

import "dotnet"

rule PureLogStealer_GUID

{

condition:

dotnet.guids[0]=="9066ee39-87f9-4468-9d70-b57c25f29a67" or

dotnet.typelib=="856e9a70-148f-4705-9549-d69a57e669b0"

}

Python tool

But what if we want to run this on a large set of samples and produce statistics, which we can then use to hunt or classify malware families, or cluster campaigns?

A newly developed Python tool will help you do exactly just that. It supports both a single file as well as a whole folder of your samples or malware repository. It will skip over any non-.NET binary and simply report the typelib, MVID and typelib ID (if present, which is seldom the case and rarely useful).


If we run it on our single sample like before:

A computer code with white text

Description automatically generated
Figure 7 - New tool output on single sample


The tool (or script) has the following capabilities:

A screen shot of a computer program

Description automatically generated
Figure 8 - Run the tool with -h to display usage or help

You need Python 3, pythonnet and a compiled dnlib.dll in order for it to work.

You are of course not limited to just using the MVID or Typelib for .NET malware hunting: you can also use the assembly name and other features that could be unique, using either the Yara rule or the Python tool to extract the data you’d like.
Both the Yara rule and the Python tool are published on the following GitHub page: https://github.com/bartblaze/DotNet-MetaData 

I highly recommend to use the tool rather than the Yara rule, as it detects .NET metadata more reliably. Both Yara rule and Python tool can be adapted to display less or more information according to your needs. 


Clustering

Tracking attacker’s campaigns is always an exercise, and can be both fun and exhausting, depending on how many rabbit holes you (want to) go through. An example of clustering campaigns as well as malware developers was done in the work I did with Santiago as mentioned earlier, which resulted in the following graphics:

A screenshot of a graph

Description automatically generated
Figure 9 - Statistics from 2016 research (bonus obfuscation stats)


This was a pretty large dataset (1.300 samples!) and specific to SteamStealers at the time.

For our analysis purposes, I took 4 of the most current popular malware (that are .NET based or have at least a .NET variant) according to Any.run’s Malware Trends: https://any.run/malware-trends/. These are:

  • RedLine

  • Agent Tesla

  • Quasar

  • Pure*: basically anything related to PureCrypter, PureLogs, …

Downloading the latest available samples per family from MalwareBazaar, then running my DotNetMetadata Python script, and playing around with pandas and matplot, we can create the following graphs per family:



RedLine – 56 samples

A pie chart with colorful circles

Description automatically generated
Figure 10 - RedLine Typelib GUID frequency


A colorful circular chart with numbers and numbers

Description automatically generated
Figure 11 -RedLine MVID frequency


Agent Tesla – 140 samples

A pie chart with numbers and a number

Description automatically generated
Figure 12 - Agent Tesla Typelib GUID frequency



A circular pattern with different colors

Description automatically generated with medium confidence
Figure 13 -Agent Tesla MVID frequency





Quasar – 141 samples


A pie chart with colorful circles

Description automatically generated
Figure 14 - Quasar Typelib GUID frequency



A pie chart with different colored circles

Description automatically generated
Figure 15 -Quasar MVID frequency




Pure* family - 194 samples 


A diagram of a pie chart

Description automatically generated
Figure 16 - Pure* Typelib GUID frequency



A circular pattern with different colors

Description automatically generated with medium confidence
Figure 17 -Pure* MVID frequency




While these piecharts are certainly hypnotic and display the frequency - or occurrence of the same typelib or MVID, we can also leverage these and create meaningful Yara rules for clustering samples per family, especially in the case of Quasar, the MVID with GUID "60f5dce2-4de4-4c86-aa69-383ebe2f504c" appears like a good candidate.

You might think that while these charts look visually appealing (depending on your art preferences), they may not be particularly useful because they don't scale well with larger datasets. You’re exactly right! By limiting the amount of results displayed, we can indeed produce even better results. In our sample dataset for the 4 malware families above, so a total of 531 samples, let’s run our visualisations again and now we will:

  • Run it on the whole sample set

  • Extract the assembly name

  • List only the top 10 of assembly names

  • Use a bar chart instead of a pie


And the result:

A bar chart with blue squares

Description automatically generated
Figure 18 - Assembly name frequency - looking better right?

The top 3 is then:

  • “Client”: Quasar family

  • “Product Design 1”: Pure family

  • “Sample Design 1”: Pure family

Client is likely the default assembly name when compiling the Quasar malware (project), and Product Design and Sample Design are likely default assembly names from the PureCrypter builder. 

If we then want to write a Yara rule for Quasar based on the default assembly name:

import "dotnet"

rule Quasar_AssemblyName

{

condition:

dotnet.assembly.name == "Client"

}


But why stop there? We can build a Yara rule to classify our malware dataset or repository:

import "dotnet"

import "console"

rule DotNet_Malware_Classifier

{

condition:

(dotnet.assembly.name == "Client" and console.log(“Likely Quasar, assembly name: ", dotnet.assembly.name)) or

(dotnet.assembly.name == "Product Design 1" and console.log("Likely Pure family, assembly name: ", dotnet.assembly.name)) or

(dotnet.assembly.name == "Sample Design 1" and console.log("Likely Pure family, assembly name: ", dotnet.assembly.name))

}


And we run this new Yara rule on the combined samples of the Pure family and Quasar:

A screenshot of a computer

Description automatically generated
Figure 19 - Simple "malware classifier"


We can combine sets of Yara rules bases on assembly name, Typelib, MVID and so on to create rules with a higher confidence, and we can use this in further hunting, classification and... much more. 


Bonus

If you’ve made it this far, it only makes sense to add in an additional extra use-case for all of this: finding new crypters or obfuscators! 

When I ran the script on the +500 samples, there was 1 assembly / binary that stood out:

A cartoon of a bathtub

Description automatically generated
Figure 20 - Potential new crypter "Cronos"

Making a simple Yara rule again:

import "dotnet"

rule cronos_crypter

{

strings:

$cronos = "Cronos-Crypter" ascii wide nocase

condition:

dotnet.is_dotnet and $cronos

}


Running this on the Unpac.me dataset yields:

A screenshot of a computer

Description automatically generated
Figure 21 - Unpac.me Yara hunt results


4 matches in 12 weeks: it appears this crypter is not popular (yet): 2 Async RAT samples and 2 PovertyStealer samples have used it so far. 


Bonus on Bonus


Let’s go with a final bonus round: improving the previous “classification” rule by also reviewing results for Async RAT. Seeing the previous crypter was used on at least 2 Async RAT samples, I wanted to see some statistics for this malware as well, for just the assembly name. This results in the following, based on 86 samples:

A pie chart with different colored circles

Description automatically generated
Figure 22 - Another pie chart: AsyncRat top used assembly names

 

Jumping out are the following assembly names:

  • AsyncClient

  • Client --> Also seen in Quasar!

  • XClient

  • Output

  • Loader

  • Stub


AsyncClient is likely the default name when building the Async RAT project. But we are interested in widening the net: from the previous rule DotNet_Malware_Classifier, let’s update it with these new “generic” or default assembly names:


import "dotnet"

import "console"

rule DotNet_Malware_Classifier

{

condition:

(dotnet.assembly.name == "Client" and console.log("Suspicious assembly name: ", dotnet.assembly.name)) or

(dotnet.assembly.name == "Output" and console.log("Suspicious assembly name: ", dotnet.assembly.name)) or

(dotnet.assembly.name == "Loader" and console.log("Suspicious assembly name: ", dotnet.assembly.name)) or

(dotnet.assembly.name == "Stub" and console.log("Suspicious assembly name: ", dotnet.assembly.name))

}




A screenshot of a computer

Description automatically generated
Figure 23 - Classifier Yara rule results


Conclusion

In this blog post, two new tools were presented to extract metadata from .NET malware samples. Specifically, we can now reliably extract 2 unique GUIDs: the Typelib and the MVID.

The Python script is capable of extracting the desired data from a large set of .NET assemblies, whereas the Yara rule is tailored for use with one particular sample. Of course, either of them can be used interchangeably: you can still fine-tune the Yara rule for a large set and work this way if you don’t want to rely on an external script. Similarly, the script can be extended to extract more data to be used.

Based on the output of these tools, you can then create Yara hunting rules, combine it with your existing rule sets, or use them in an attempt to classify malware families or specific attack campaigns.

Some closing remarks:

  • GUIDs could be spoofed or even removed. No method is 100% reliable.

  • However, this method can enhance already existing rulesets, especially those where .NET obfuscators (e.g. SmartAssembly) obfuscate (user) strings, modules and more, making it harder to write Yara rules for a malware family. Detecting based on GUID however, can work regardless of obfuscation method.

  • That said, obfuscating or deobfuscating may also alter the GUIDs. Keep this in mind when creating your detection rules based on an original or unpacked/deobfuscated sample.

  • If you encounter a GUID comprised entirely of zeros, such as 00000000-0000-0000-0000-000000000000, avoid using it for hunting since it's an empty GUID. This indicates the value may not be set or has been altered. This would make for a poor hunting rule as it can be a default value for any .NET project.

  • You can also use this methodology and tooling for .NET assemblies that are not malicious: extract developer information and other metadata per your use case or purpose.

    The Python tool in addition, just as the Yara rule, allows for analysing, classifying and hunting on much more .NET (meta)data.

     

Happy .NET hunting! You can find the tools and some of the example Yara rules in the repository: https://github.com/bartblaze/DotNet-MetaData 

As always, feedback is welcomed.


Fake Steam Desktop Authenticator steals account details

By: Bart
25 February 2018 at 23:44

In this blog post, we'll have a quick look at fake versions of Steam Desktop Authenticator (SDA), which is a "desktop implementation of Steam's mobile authenticator app".

Lava from SteamRep brought me to the attention of a fake version of SDA floating around, which may be attempting to steal your Steam credentials.

Indeed, there are some fake versions - we'll discuss two of them briefly.


Fake version #1

The first fake version can be found on steamdesktopauthenticator[.]com. Note that the site is live, and appears at the top of Google Search when searching for "Steam Desktop Authenticator".

Figure 1 - Fake SDA website













When downloading the ZIP file from the website, and unzipping it, we notice the exact same structure as you would when fetching the legitimate package - with one difference: the main executable has been modified.

File details:
Name: Steam Desktop Authenticator.exe
MD5 hash: 872abdc5cf5063098c87d30a8fcd8414
File size: 1,4446 KB
Version: v1.0.9.1

Note that the current and real SDA version is 1.0.8.1, and its original file size is 1,444 KB - 2 bytes of difference can mean a lot. Figures 2 and 3 below show the differences.



Figure 2 - Sending credentials to steamdesktopauthenticator[.]com

















Figure 3 - Sending credentials to steamdesktop[.]com






















Indeed, it appears it also attempts to upload to another website - while digging a bit further, we can also observe an email address associated with the domains: mark.korolev.1990@bk[.]ru

While I was unable to immediately find a malicious fork with any of these domains, Mark has likely forked the original repository, made the changes - then deleted the fork. Another possibility is that the source was downloaded, and simply modified. However, it is more than likely the former option.



Fake version #2

This fake version was discovered while attempting to locate Mark's fork from the fake version above - here, we have indeed a malicious fork from GitHub, where trades/market actions appear to be intercepted, as shown in Figure 4 below.

Figure 4 - Malicious SDA fork (click to enhance)











Currently, when trying to access the malicious site lightalex[.]ru with a bogus token, a simple "OK" is returned - it is currently unknown whether market modifications would be successful.

Interestingly enough, when digging deeper on this particular domain, which is currently hosted on 91.227.16[.]31, it had hosted other SteamStealer malware before, for example cs-strike[.]ru and csgo-knives[.]net.

The malicious fork has been reported to GitHub.



Disinfection

Neither fake SDA versions reported here appear to implement any persistence, in other words; remove the fake version by deleting it, and perform a scan with your current antivirus and a scan with another, online antivirus, or with Malwarebytes for example.

Additionally, de-authorize all other devices by clicking here and select "Deauthorize all other devices".

Now, change your password for Steam, and enable Steam Guard if you have not yet done so.



Prevention

Prevention advise is the usual, extended advise is provided in a previous blog post here.

You may also want to take a look at SteamRep's Safe Trading Practices here.

Always download any software from the original source - this means the vendor's website, or in this case, the official SDA repository on GitHub:
https://github.com/Jessecar96/SteamDesktopAuthenticator



Conclusion

SteamStealer malware is alive and well, as seen from my January blog post. This is again another form of attempting to scam users, and variations will continue to emerge.

Follow the prevention tips above or here to stay safe.


Indicators


Malware Analysis, Threat Intelligence and Reverse Engineering: workshop slides

By: Bart
8 February 2018 at 21:31

Last month I gave a workshop for a group of 20-25 enthusiastic women, all either starting in infosec, or with an interest to start in this field.

For that purpose, I had created a full workshop: slides or a presentation introducing the concepts of Malware Analysis, Threat Intelligence and Reverse Engineering.

The idea was to convey these topics in a clear and approachable manner, both theory and in practice; for the latter, I had set up a custom VM, with Labs, including my own created applications, some with simple obfuscation.

All participants were very enthusiastic, and I hope to have sparkled most, if not some of them to pursue a career in this field. For this exact same reason, I am now releasing the presentation to the public - the VM and recordings however will not be published, as I created these solely for CWF.

You may however download the LAB material from Github below:
https://github.com/bartblaze/MaTiRe

Without any further ado, you may find the slides below, on SlideShare:


Any feedback is always appreciated.

I would also like to thank Nathalie for putting me in touch with Rosanna, the organiser of the CyberWayFinder program. And of course, my gratitude to all the attendees for making it!

Mind the disclaimer for the slides. License: CC Attribution-NonCommercial-NoDerivs License
❌