Between a rock and a hard place - Exploring Mount Locker Ransomware

Post image

Hey there, long time no blog post :D It’s not like I haven’t been doing any research the last couple of months, but between the whole Covid-19 Situation, University work and everything else there was either too little time to finish the posts I started or I came up with multiple smaller things like my Netwalker Config Extractor that would not justify a dedicated blog article (I normally dump stuff like this on Twitter instead).

Anyway, I wanted to get atleast one more Blogpost out before 2020 comes to an end. This time I will be looking at “Mount Locker”, yet another Double Extortion Ransomware (as a Service) that is targeting Microsoft Windows. The goals of this article are:

  • Highlight the Unpacking of the samples
  • A bit more debugging, something that my older posts lack
  • A walkthrough of the cryptographic functions
  • A quality Yara Rule

Back in September NHS Digital already released an alert for Mount Locker dating its first appearance to July 2020. Although they do not state a delivery method at the moment access through stolen Credentials and Remote Management are very likely. The Advisory also features very solid remediation advice.


News & Leak Sites

Since we are talking about Double-Extorition Ransomware here of course Mount Locker runs its own Leak Site. It is available via Tor only and features a Home page, a contact formular and a blog style listing of compromised companies they hit and stole their data.


Next, let’s have a look at how they communicate with their victims. All of the dropped HTML Ransomnotes contain a unique-per-sample onion URL with a live chat. Browsing to this Tor site without the Client ID specified like this /?cid= you will end up with the login prompt shown below (victims don’t get a password and leaving the input field blank does not work, so this might only work for operators/affiliates?). I went ahead and removed the Client IDs from all screenshots to not lower the bar for people trying to mess with the chat in any way. Same with the additional contact E-Mail addresses that were found in two of the sample Ransomnotes which follow the pattern firstnameLastname[year/integers]


I will include two of the support chat logs as well, because they allow for a few interesting insights into how Mount Locker attacks work. In the Screenshot below you can see a monologue by the Mount Locker Operators. They obviously really like the support the “Tech Support Role” since they open up the conversation with “We are ready to help you. What are your problems?”. After over a week with no reply from the victim they try to contact them again by adressing the message directly to an employee (name redacted, classic scare tactic). It’s unclear how they got the name, but my guess is they either gained access via Malspam or remote access to the employee’s account.

Chat 1

In the second case the conversation went on for much longer. This time the victim more or less confirms that the affected server was compromised through internet-exposed Remote Access. Upon asking for a decryption price they return with a price of 10 BTC. If this is their default price for an individual they likely won’t get anywhere with their ransom demands, since BTC is on an all-time high as well. The criminals later discount this price to 5 BTC which is still too much and the victim decides not to pay (good choice).

Chat 2

Just for fun I took a quick look at their sites code and noticed their 404 page displayed the nginx version in use. Both of the chat portals from Mount Locker Version 2 (we’ll come to the specifics later) likely use nginx 1.14.2. Would you like to take a guess when that version was released? How does over 2 years ago (04.12.2018) sound? Their main Leak site does not display a version number, but I guess they really prefer legacy stuff 👴


Initial Analysis

Both of the samples I will be focusing on in this post belong to “Version 2” of Mount Locker, because since the initial Version of the Ransomware turned up in Sandboxes and Analysis platforms, there were some major changes made by the operators of the campaign.

Sample 1

filename:   BreakOut.exe, XACQDAPEV.exe 
filesize:   200KB
sha-256:    226a723ffb4a91d9950a8b266167c5b354ab0db1dc225578494917fe53867ef2
ssdeep:     1536:ssBoz9GFuIdclwKfVPoawSL20mRbg2DrE1mHkrY0f3r6fR0ZzDWR+3itGSh6ZVvg:ssS3oifBoaXhDWA4G3eeJaeIbmC00
VT First Submission: 11.11.2020 15:26:06 

Download: AnyRun | Malware Bazaar | VirusTotal | HybridAnalysis

Sample 2

filename:   QuantumQuditSimulator.exe
filesize:   532KB
sha-256:    e7c277aae66085f1e0c4789fe51cac50e3ea86d79c8a242ffc066ed0b0548037 
ssdeep:     6144:Q5fW8eILySdSS4JoHjnJVZJQQIreKsuKu3a2WQe0gz+Y:OeILzSS5jnJ/JTu3zWtqY
VT First Submission: 18.11.2020 19:33:26 

Download: AnyRun | Malware Bazaar | VirusTotal | HybridAnalysis: unavailable

As always we’ll start out with a quick look at Detect it easy to see what we are working with. In both cases it detects Virtual Basic 6 yay! Checking the imports to be sure: yep they only Library we see is MSVBVM60.DLL, the Microsoft Virtual Basic Virtual Machine (yo dawg, heard you like virtual things :D). Although the entropy graph didn’t directly indicate a packer I doubt that a serious RaaS Group would write Ransomware in VB.6 these days.

Detect it easy, packed

The next step is to check out the Resources with Resource Hacker. Let’s start off with one of the embedded String Tables. The marked function names are definetely a red flag and indicate that this is in fact a VB.6 packer.


The second curious thing is a PE File! But wait, take a closer Look. There is something wrong with the DOS and Rich Header. The “DOS String” should normally read “This programm cannot be run in DOS mode." whereas this one reads “This!prohsam!caolpt be tum gm DOS kndc.". 🤔 Looks like some of the characters were shifted by one and then a few were skipped. But instead of figuring it out by hand we’ll take a look at the code next and unpack it, since this is just a diversion after all.


Because VB.6 looks absolutely horrendous in IDA or Ghidra we’ll have to use a more specialized tool, VB.Decompiler Pro in this case. Upon opening the samples with it we notice that most of the implemented functions are dead code. This is also apparent when we look into the GUI Designer (see below), because there is a Graphical User Interface that we never get to see upon running it in a Sandbox. Sample 1 seems to be a game called “Break-Out” and Sample 2 is called “Quantum Qudit Simulator”, some kind of calculation programm from the National University of Ireland. Of course both of the Developers of the original applications do not have anything to do with Mount Locker Ransomware, because these samples were obviously altered to act as a packer for it. Even after a lot of search engine magic I couldn’t find out where they got these programs from, but if you find them anywhere let me know!

Backdoored Programs

Now let’s look at some decompiled VB.6 code! The API declarations below basically reveal what we already saw in the string table in the resources.


I’ll try to present the next lines of snuck-in code in a logical order. The first step is to load the PE resource we saw earlier.


Up next is a long boi call to VirtualAlloc (sorry if the font in the screenshot is a bit small). Also take note of the variable names like var_804C (which sometimes indicates this code was inserted at a later date) and the obfuscation of the arguments to VirtualAlloc.


Last but not least we have a call to VirtualProtect and RtlMoveMemory (renamed to CopyMemory in the API Declarations above), again with the same obfuscation, to change the protection of the allocated Region in memory and moving the payload there. This is as far as we are going to look into this right now though, since this is just supposed to be a quick look into this “backdoored” application and we’ll continue by unpacking the Ransomware sample now.



Since I’m pretty lazy and a big fan of Open Analysis Live I thought I’d give a chance at this packer first. Unfortunately something went wrong here (I did not expect more than one Child Binary), but that has to be expected since Classifier Issues with VB.6 Code is one of the Challenges that they are still working on.


That should not deter us from unpacking it manually though! The easiest and fastest way would be to set a breakpoint on CreateProcessInternalW and dump it from there if applicable.

Again, sadly this does not what we want here. Apparently this sample utilizes Self-Injection, so the call to CreateProcessInternalW won’t take us to the unpacked Child Process but rather a Powershell script. This seems to be part of the actual Ransomware rather than the packer, so we are too far in already.


I’ll have to do it the old fashioned way then. One Breakpoint on VirtualProtect and one on the Return from VirtualAlloc. For good luck I’ll also throw in one on IsDebuggerPresent.


Turns out altering the return value of IsDebuggerPresent is not necessary for Sample 1, it just unpacks the child regardless. Sample 2 on the other hand will throw an error message and terminate.


The disadvantage of breaking on VirtualProtect or VirtualAlloc in the case of VB.6 is that the Microsoft Visual Basic Virtual Machine calls both of them multiple times in the setup process. Instead of “Following in Dump” every time we hit a breakpoint it is far easier to just look for the jump to the unpacked PE. The fastest way to get you there is placing a breakpoint on VirtualFree which (atleast in case of these two samples) is called right before the function call that jumps into said Ransomware PE. Once you are there, step into the function and follow the address in jmp ecx to find the unpacked sample. Below I also included a screenshot (green border, blue/white font) of the packed sample in a hex-editor to show that the binaries actually differ.


A quick look at the memory map before we’ll dump the segment and go right ahead.


Of course the first thing after dumping the binary for me is to throw it into PE-Bear. Looks like it is still in it’s mapped state, so let’s continue by unmapping it. (Screenshot: left = mapped, right = unmapped)


One more thing: Don’t forget to adjust the Image Base or your Disassembly/Decompilation results will look like garbage. Ask me how I know 😅

Image Base Adjustment

Just to verify that we (likely) don’t have another packed stage here I’ll take a look at it with Detect it Easy. Seems good, let’s continue with Static Analysis!

Detect it easy, unpacked

Static Analysis

First let’s check the compiler timestamps on the samples we have. Both x86 binaries contain the same one from November 6th 2020 10:47:05 UTC. Interestingly the x64 Version was (allegedly) copiled just one second earlier.

Compile Time

To make a list of what to expect from this Ransomware I would normally go through the Imports and make some more or less educated guesses what it would do with those functions, but this is again a perfect opportunity to try out a new tool: capa by Fireeye. It was released about 6 months ago and it looks like I’ve been really missing out. As you can see below I made one minor correction after I finished the analysis, but besides that the tool is pretty spot on and will definitely save you some time in triage-like situations.


Because it will be easier to “navigate” the functions of the Ransomware if you know in what context and point in time the log messages are printed I extracted them all. Quite interesting to see this level of verbosity in a Ransomware strain that has been deployed in a few attacks already in the last months. It will become even more apparent in the course of this analysis, but the developers are obviously still trying to figure things out.


Alright, enough 🦆-ing around, let’s dive in! Ghidra 9.2 is the tool of choice this time around since I wanted the comfort of a decompiler. To start off I took a look at it function-by-function and renamed them to reflect what each one of them does. The following screenshots will show most of the peripheral functions and setup-related things. The Crypto functions will be discussed in a separate chapter. Something to watch out for: there is quite a lot of variable re-use happening in certain functions, so I either renamed the variable to something that would reflect the value it was asigned last or I kept the original name generated by the decompiler to avoid confusion.


We’ll start at the entrypoint. From here the sample branches off into two functions: mw_getArguments will handle, as the name suggests, the arguments given by the operator and mw_mainLockRoutine from where multiple peripheral functions are called.


Given that the Ransomware sample accepts commandline arguments it speaks for the asumption, that Mount Locker is designed to be manually operated by criminals. The screenshot below depicts the function mw_getArguments that handles the supplied commandline options:

  • /log: –> can be used with ‘F’ or ‘C’ to write a Log to a File or Console respectively
  • /scan: –> valid options: L | N | S, scan attached drives where L = Local Drive, N = Network Drive, S = Network Share
  • /marker: –> create a specified marker file on each volume to be encrypted
  • /nodel: –> do not delete the Ransomware binary after execution


The third function we will take a look at is what I would call the “Main Routine”. From here a lot of substantial functions of the Ransomware are called. After allocating the debug console (if the argument flag was set) it will check if a Mutex from another Mount Locker process was already set. Before the actual file encryption functions are called it will delete the Volume Shadow Copies and terminate running processes.


1) To make sure the Ransomware only runs once on a particular system it will create a Mutex that is derived from the Volume Serialnumber of the System Drive e.g. 1AB6AEEA4356D5DDA86ADABB750D5B57. If it fails to retrieve the Serialnumber via GetVolumeInformationW it will default to a Backup value: 0x41a207bd which is permuted in the same way. Should CreateMutexW fail and return NULL or System Error 0xb7 (ERROR_ALREADY_EXISTS) the mw_mutex function return false, write an error message to the log and the Ransomware will terminate. Otherwise the function will return true and the execution continues.


2) Up next we have a Powershell script that is written to %temp% (Path via GetTempPath) with the Filename determined via GetTickCount and the extension .tmp. In the next step we’ll check what it actually does.

Powershell Function

For decoding and decompressing the Powershell script the most popular tool is of course Cyberchef. Because I like to try out new/alternative tools we’ll use Binary Refinery today! It is a great substitute for Cyberchef in Malware Analysis/Triage situations and you can also use it as a Library for your Python projects / tools. And don’t forget about the extra street cred for using a CLI tool 😎 As you can see in the screenshot below the first half of the Powershell script is used to delete the Volume Shadow Copies via vssadmin.exe and to stop all services that don’t run from the Windows System Directory.


I shortened the process exeption list, but in total it contains 657 filenames of Webbrowsers, System Tools, a lot of Anti-Virus and EDR Clients and even ollydbg.exe, how nice of them! (or maybe they use it internally for debugging? 🤔). Every process running on the system that does not belong to the Ransomware, run from the Windows directory and is not on the Exception List will be terminated.

Process Kill

3) In line with their verbose logging functionality Mount Locker will separate its log output by the volume type of the targeted drive. The interesting “discovery” in this case is that the encryption of Network Shares is not supported yet and therefore Mount Locker won’t proceed with file encryption on such volumes.

Network Share

The Log strings in the mw_lockerDirCheck also allow for interesting insights into features into (upcoming) features. For one the are messing around with NTFS Reparse Points which is rarely seen in Malware and log messages like “[OK] locker.dir.check > target_hidden” indicate that they are also trying to attack hidden directories.


The generation of the ClientID is based on the return of GetComputerName which is used with a 32 character hardcoded string to compute the 64 character long ClientID String found in the Ransomnote.


Mount Locker ships with a list of Directory Paths and extensions to be spared from encryption to keep the Operating System … operational. We’ll see in the dynamic Analysis Chapter how well that actually works.


To constantly remind the victims of the Ransom and increase psychological pressure Mount Locker registers the Ransomnote to be opened when the user tries to access an encrypted file. The lower screenshot is taken from the Hatching Report for Sample 2 which you can find here.


The second file that is written to %temp% during the execution of Mount Locker is yet another one with its name derived from the return value of GetTickCount. After the Ransomware finished the encryption routine and if the /nodel flag was not supplied it’s time to clean up and Mount Locker will delete itself by invoking e.g. cmd /c "C:\Users\admin\AppData\Local\Temp\\0F7565F4.bat" "C:\Users\admin\Desktop\mountlocker.exe".

BAT File

Cryptographic Functions

Another useful Tool for Ransomware Analysis is PEid with the KANAL plugin. KANAL is short for “Krypto Analyzer” and tries to detect crypto algorithms, functions and constants. In the case of Mount Locker it only recognizes the RSA Public Key import via the WinCrypt API.


As the function name suggests mw_importPubkey imports the RSA Public Key that is ebedded into the binary. This function also generates the Session Key used with ChaCha20 via __rdtsc + Sleep (more on that later) which is encrypted with the RSA using CryptEncrypt. Once that is done the ClientID will be generated and the Ransomnote is registered to opened with the file extension of the Ransomware.

RSA Routine

To take a closer look at the supplied RSA Public Keys I dumped them from both samples with x32dbg.


Just as a quick “sanity check”: Yes, the Public RSA Keys differ between the samples. Wouldn’t be the first time that lazy Ransomware operators reused the same RSA Keypair in multiple samples, so I think it’s worth to check atleast.


In the screenshot below we see the ChaCha20 Block function which is an important component of the Algorithm. It can be identified by the expand 32-byte k magic value.


Since the folks at Blackberry ThreatVector already spilled the beans, I’ll mention it here as well. Instead of using a secure Random Number Generator for the File (and Session) Encryption Keys they opted to use __rdtsc, which returns the processor time stamp (clock cycles since the last reset) without a Sleep call. The Time Stamp Counter is a bad way to generate encryption keys because it is a deterministic function that wraps around every ~49 days and could therefore theoretically be bruteforced (with knowledge about the point in time when __rdtsc was invoked, the Sleep call would be used to “obfuscate” this). This is nothing new though and with the coverage Mount Locker has received up until now I expect this issue to be fixed by the next version.


I don’t want to throw shade here, but I’m not a fan of detailing Cryptobugs in ongoing Ransomware campaigns. The use of GetTickCount (__rdtsc) isn’t a huge flaw in itself, but it could very well come in handy if there were more flaws in the crypto-implementation used. I strongly recommend to watch @Polartoffee’s Talk from Steelcon2019 where this is addressed as well (the video below is timestamped for your convenience):

Fun-fact, speaking of Cryptowall: The File extension list in Mount Locker Version 1 contains about 2600 entries. It made headlines because it also targets Tax Software (which isn’t a common thing, but it has been seen before on several occurrences). What I find far more interesting is that they target files previously encrypted by other Ransomware strains as you can see below:

Ransomware Extensions

MapViewOfFile is used to map each file into Memory for encryption. Smaller files are mapped according to their size and if a file is larger than 0x40000000 it will be mapped in chunks and encrypted.

RWrite Keys to File

To further investigate the File Encryption I used the classic trick of the bait file filled with null bytes to check for patterns and appended data. As we can see the encrypted file is now 288 bytes longer. They can be divided into 32 bytes for the File Key (which is, as the name suggests, unique for every file) and 256 bytes for the Session Key. Of course these keys are not prepended to the fileheader like that: the File Key is encrypted with the Session Key using ChaCha20 and the Session Key is encrypted with the public RSA Key through CryptEncrypt.

Testfile plain
Testfile encrypted

This can be confirmed with Ghidra: Both encrypted keyblobs are prepended to the file with WriteFile.

Map File

Since most Ransomware groups don’t try to reinvent the wheel when it comes to crypto implementations they often get their inspiration from Open Source projects. I looked around Github for a while to find C++ Implementations of ChaCha20 that would fit what we see in Mount Locker. The Repository linked below is just a guess at what they could have used, since this seems to be the oldest ChaCha20 C++ Repo on Github and many Implementations borrow code from it. The structure and compartmentalization into functions also looks very similar. Berstein’s reference Implementation for example is constructed differently. I crossreferenced it with RFC8439 and it looks solid at first glance. Parts of the Quarter Round Mechanism and the Block function are directly copied from the Wikipedia article on Salsa20/ChaCha20 though and the code comments are a bit too sketchy IMO 😂

Github ChaCha20 Implementation

Dynamic Analysis

Let's first take a look at the Ransomnote. In all investigated versions of Mount Locker it is delivered as a HTML file named *RecoveryManual.html*. It exhibits a lot of the by now classic scare tactics we know from Ransomware gangs in the Realm of "Don't try to decrypt your files, we are the only ones who can help" and of course the Threat that they would release the stolen data if the Ransom would not be payed in time. Communication with the criminals is to be done through a Webchat via Tor with no Clearnet alternative except for contacting them by E-Mail (accounts registered with Protonmail, which is still the most popular Mail hosting service for Ransomware operators. See []( for more information).

During the “Detonation run” I opened Process Monitor for a better Overview of what’s going on. Because I didn’t want Mount Locker to terminate it, I renamed it to firefox.exe since this process name is on the exception list 👍 As you can see below the Ransomware has four Subprocesses in total: Powershell + vssadmin for process termination and restore point deletion and cmd + attrib for self-deletion.

Process Graph

I ran Mount Locker with the */log:F* argument to have it log to a file. It created a file on the Desktop named *executableName.exe.log*, the contents of which can be seen below. The first few lines are related to the deletion of the Volume Shadow Copies. Since Mount Locker currently is not capable of escalating priviledges and I ran the sample without administrative rights the Shadow Copies were left untouched.

Since it is probably one of the most interesting debug features of Mount Locker here is another snippet from the Log File showing the statistics of the System Drive Encryption.

Log Stats

And if you would like to know what happens when Mount Locker V2 is run with local admin priviledges, because I already hinted that it doesn’t go well: After the machine is rebooted (which some Users do instinctively after a Ransomware infection) Windows will not be able to boot because the bootmgr file was encrypted. I’m not sure if they didn’t test this case or they forgot to include it in their exception lists, but the User will certainly not be able to contact them with a (temporarily) bricked machine.


Conclusion / Key Takeaways

  • Up until now only Mount Locker V2 x86 binaries have been submitted to analysis platforms in a packed state. Similar to other Ransomware Gangs they opted to deliver their x64 executables as DLLs.
  • No Obfuscation beyond the initial packing
  • No Persistence Methods used
  • Depending on the Users permissions: Mount Locker either fails to delete Shadow Copies and files on other drives or it will encrypt critical system components and render the system unusable after a reboot
  • Multiple features are not implemented in Version 2

The Operators behind Mount Locker are obviously just starting out, as the observed Ransomware samples are still very verbose and noisy on execution. Although the are not nearly as active as a lot of other well established Crimeware Groups, I expect to see more attacks from them in the upcoming year 2021. I hope this blog post will be somewhat helpful in future investigations on this Ransomware strain and of course I will be keeping up with it as well.

Alright, that's it for now. Thank you for reading this blog post! If you have got any feedback please let me know and don't miss the Yara Rule, Mitre Att&ck List and IoCs below 🤓 See ya in 2021 👋

Yara Rule

import "pe"
rule RANSOM_MountLocker_V2 { 

  description = "Detects Mount Locker Ransomware, Version 2 x86 unpacked" 
  author = "Marius 'f0wL' Genheimer <>" 
  reference = "" 
  date = "20.12.2020"
  tlp = "WHITE"
  hash1 = "226a723ffb4a91d9950a8b266167c5b354ab0db1dc225578494917fe53867ef2"
  hash2 = "e7c277aae66085f1e0c4789fe51cac50e3ea86d79c8a242ffc066ed0b0548037"

  //picks up on the Volume Serial Number Permutation in function mw_mutex
  $mutex_shift = { 8b c1 c1 c8 ?? 50 8b c1 c1 c8 ?? 50 8b c1 c1 c8 ?? 50 51}

  $x1 = "powershell.exe -windowstyle hidden -c $mypid='%u';[System.IO.File]::ReadAllText('%s')|iex" fullword wide
  //$x2 = "explorer.exe RecoveryManual.html" fullword wide
  $x2 = "RecoveryManual.html" wide

  $x3 = "expand 32-byte k" fullword ascii
  $x4 = "<b>/!\\ YOUR NETWORK HAS BEEN HACKED /!\\<br>" fullword ascii

  $s1 = "[SKIP] locker.volume.enum > readonly name=%s" fullword wide
  $s2 = "[WARN] locker.dir.check > get_reparse_point gle=%u name=%s" fullword wide
  $s3 = "[ERROR] locker.file > get_size gle=%u name=%s" fullword wide
  $s4 = "[OK] locker > finished" fullword wide

  uint16(0) == 0x5a4d and filesize < 600KB
  and pe.imphash() == "1ea39e61089a4ea253fb896bbcf01be5"
  and $mutex_shift 
  and 2 of ($x*) 
  and 2 of ($s*)


T1012 –> Query Registry –> Discovery

T1027 –> Obfuscated Files or Information –> Defense Evasion

T1055 –> Process Injection –> Privilege Escalation, Defense Evasion

T1059 –> Command and Scripting Interpreter: PowerShell –> Execution

T1070 –> Indicator Removal on Host: File Deletion –> Defense Evasion

T1076 –> Remote Desktop Protocol –> Lateral Movement

T1082 –> System Information Discovery –> Discovery

T1083 –> File and Directory Discovery –> Defense Evasion

T1112 –> Modify Registry –> Defense Evasion

T1129 –> Shared Modules –> Execution

T1134 –> Access Token Manipulation –> Defense Evasion, Privilege Escalation

T1486 –> Data Encrypted for Impact –> Impact

T1489 –> Service Stop –> Impact

T1490 –> Inhibit System Recovery –> Impact

T1546 –> Event Triggered Execution: Change Default File Association –> Privilege Escalation, Persistence

T1562 –> Impair Defenses: Disable or Modify Tools –> Defense Evasion


Mount Locker

Version 1    

Version 2



Associated Files

[8-digit hex].bat - Batch script
~[9-digit int].tmp - Powershell script
--> both of these files will be dropped in C:\Users\username\AppData\Local\Temp\


Onion URLs


You May Also Like