STRRAT (Strigoi) - Malware Analysis Lab

68 minute read

STRRATPicture

Overview

Part 1: STRRAT Dropper and STRRAT Deobfuscation

Let’s take a look at a recent sample of the Java-based malware known as STRRAT. We will cover some techniques on how to identify the malware and reverse engineer it. We will start with a malicious JAR file being distributed through email malspam campaigns, and in this instance the file had the name INQUIRY______535262623.jpg.jar

First off we obtain a sample with a particular SHA256 hash:

Starting Host IOC: ec48d708eb393d94b995eb7d0194bded701c456c666c7bb967ced016d9f1eff5

Given this is a Java-based RAT we can start by opening it using JD-GUI, a Java Decompiler.

If we expand out the only class this contains, we immediately begin to see references to its resource section, wscript, and “user.home”. Given these references, and the lack of classes, this is a good indicator that the Java Archive will likely function as a dropper, placing a script with the name “loorqhustq.js” in the users home directory.

Trojan Dropper Decompiled

To understand the script being dropped to “loorqhustq.js”, we can take a look at the resource section by extracting the Java Archive using 7-Zip.

Trojan Dropper Resource Section

At a glance this isn’t too large; however, it does have a Base64 blob which looks to be present and some data manipulation to ensure this can’t easily be decoded. One way to tackle this is to replace references of “eval” (which is essentially telling JavaScript to execute and evaluate what it is being passed, and instead make this “console.log”. By doing this the malicious JavaScript will instead log what it is being presented, rather than processing and evaluating it.

To do this we still need a way to interpret the JavaScript in a safe setting. By opening up Google Chrome and pressing “F12” we can view the console. At this point we can copy in our modified JavaScript and have Chrome interpret it for us.

Modified JavaScript

The end result is a variable being defined containing an object called lmao$$$_. This is an ActiveXObject storing a Base64 encoded string; however, because we ran this inside of a browser and replaced the eval statements, we get an Uncaught ReferenceError. This is expected as we didn’t want the dropper to progress, and only wanted to extract this Base64 string.

Reassembled JavaScript

By copying solely the string into CyberChef, we’re able to begin extracting what will be dropped.

CyberChef Decoded Base64

Examining this decoded string reveals a variable which is being defined as “longText”, before being manipulated to make a valid Base64 string which is once again decoded to disk, this time in the user’s application data directory to a file called r.txt

Decoding Base64 String

Before we look into extracting ‘r.txt’, we can continue to look at what is happening when the dropper triggers. Examining the first part of the dropper, we find it is enumerating if the Java Runtime Environment is present, and where it is, it will attempt to execute the dropped “r.txt” as a Java Archive, if not, it will run a function called “GrabJreFromNet”.

Enumerate JRE

Looking at the function GrabJreFromNet, we can see that it attempts to reach out to hxxp[://]wshsoft[.]company/jre7[.]zip to pull down a version of JRE. This is then saved to a zip file in the user’s application data directory before it is extracted to disk, used to run r.txt, and a run key is setup for persistence to ensure it always runs whenever that user logs on.

Download JRE and Persist

Network IOC: hxxp[://]wshsoft[.]company/jre7[.]zip
Registry Run Key Persistence (Host IOC): HKCU\Software\Microsoft\Windows\CurrentVersion\Run\ntfsmgr

At this point we can go ahead and take the first 4 lines of this second stage dropper, and add an entry to once again log to the console the contents of “longText” which will be decoded to r.txt as a Java Archive and run.

Decond LongText

The output is a “PKZIP” archive, which we can identify through the presence of a MANIFEST.MF as a Java Archive.

Reveal Java Archive - CyberChef

We’re able to download this file to disk and retrieve the new hash of this Java Archive using PowerShell.

get-filehash .\STRRAT.jar
0A6D2526077276F4D0141E9B4D94F373CC1AE9D6437A02887BE96A16E2D864CF

Download JRE and Persist

Host IOC: 0A6D2526077276F4D0141E9B4D94F373CC1AE9D6437A02887BE96A16E2D864CF

By checking the hash against VirusTotal we can see it hasn’t been analysed and shared publicly yet.

VirusTotal Lookup

To help ensure AV vendors are aware of this STRRAT sample, we can submit it to VirusTotal for scanning.

VirusTotal Submission

27/60, not a bad rate of detection, noting this doesn’t represent all detection capabilities by vendors, and just because a vendor doesn’t flag it here, doesn’t mean they won’t detect or prevent it in production.

Opening STRRAT in JD-GUI, we can take a look at the manifest file to find the ‘Main’ class which will be executed when the archive is run.

STRRAT Main Class

By examining the main class we get a lot of obfuscated classes, and references to “StringBuilder” being leveraged. The string ‘STRRAT” is also present once we deobfuscate this sample.

Obfuscated String Class

At this stage deobfuscating the code manually would be a bit of a challenge. Luckily we have java-deobfuscator available to us which can perform a lot of the heavy lifting.

To use this we will first create a yaml file called detect.yml which will be used to detect what obfuscator is in use.

input: C:\Users\User\Desktop\STRRAT\Stage 2\STRRAT.jar
detect: true

From here we can pass this as the configuration to deobfuscator, and find that this looks to be using “Allatori’s Java Obfuscator - String Encryption”.

java -jar deobfuscator.jar --config detect.yml

Detect Obfuscator Used

The irony is that the website providing this tool is calling us a “hacker” for reverse engineering the Java Archive.

Obfuscating Software for Purchase

From here we can create another file set to deobfuscate the Java Archive in question using the first recommended transformer.

input: C:\Users\User\Desktop\STRRAT\Stage 2\STRRAT.jar
output: C:\Users\User\Desktop\STRRAT\Stage 2\STRRAT-Deobfuscated.jar
transformers:
	- com.javadeobfuscator.deobfuscator.transformers.allatori.StringEncryptionTransformer

After executing the transformer in question we’re presented with a number of “decryption methods”, and “encrypted strings” being rectified. In this instance there’s some output errors also, but this isn’t of concern.

java -jar deobfuscator.jar --config config.yml

Deobfuscating STRRAT

At this point we can see a number of Java Archive files being referenced:

Plugin Modules

Of these referenced includes a project “system-hook” which is essentially being used here as a Java-based key-logger.

System Hook

Examining the Main Class further we can now find reference to a pastebin URL being contacted. This gives us some potential C2 infrastructure leveraged by this malware. It should be noted this can be modified by the Pastebin user to point elsewhere.

Adversary Infrastructure

Dynamic Infrastructure (Network IOC): hxxps[://]pastebin[.]com/raw/Jdnx8jdg
Pastebin Actor Account (Network IOC): hxxps[://]pastebin[.]com/u/wshsoft

At the time of writeup this page had been visited 32,736 times, and pointed to the domain pluginserver[.]duckdns[.]org

Current Infrastructure (Network IOC): pluginserver[.]duckdns[.]org

In addition there’s a hardcoded URL mentioned which looks to contact the below:

Network IOC: hxxp[://]str-master[.]pw/strigoi/server/ping[.]php?lid=

Taking a look at the class cbnfdhn.class, we can get a feel for some potential functionality of STRRAT including shutting down an operating system, uploading and downloading files…

STRRAT Features

Enumerating the OS, processes, key-logging, stealing saved credentials.

STRRAT Features

In addition there’s a reference to “crimson_info.txt” which has been publicly reported as a fake ransomware module.

STRRAT Features

Part 2: Extracting and Decrypting STRRAT Configuration

To perform more thorough analysis on the deobfuscated STRRAT, we’re going to leverage a tool called ‘Recaf’. This will allow us to decompile Java classes in a more user-friendly way given the malware authors have reused class and method names. Upon decompiling this we’re going to take a look at the primary entry point of the program in the “Main” class.

An easy way to find this class is to search for “void main” because the main class method isn’t intended to return any output. In the below we see a watermark added to the malware to show that “Allatori Obfuscator 7.3 DEMO” was used to obfuscate it. In addition we can see it is checking for the number of arguments provided, and where this isn’t provided it will run the Main.bsgshsbs method (which is returning an object to be created from the ‘dfghrth’ class). Further to this we can see a message of “This PC is not supported” being returned after this if a string isn’t able to be assigned from the output of Main.sdfsldf. To find the right method definition here we can search for “String sdfsldf()” given we know it is returning a string, and here we can see that it is simply looking for the path the running JAR file is running from (likely taken from this StackOverflow post).

STRRAT RE

Examining the object created from the “dfghrth” class reveals this is a file lock being created in the user’s home directory. If there are command-line arguments added this will create a file lock with the name “64578lock.file” in the user’s home directory given this number is passed as a parameter when creating this lock object.

STRRAT RE

Host IOC: 64578lock.file

This serves as the port that the RAT will connect back to.

In the case where no command-line arguments are provided, this will first create an object called “object2” from the Main class method “bsgshsbs”. To make finding these references easier, we’ll begin right clicking them and using “Goto definition”.

STRRAT RE

We can also reveal references to this particular method by instead clicking “Search references”.

STRRAT RE

Examining this method we can see that it looks to be taking in a stream from a file within its resources called ‘config.txt’, Base64 decoding it, and then it passes this and the string “strigoi” to another method, this time within the sabretb class called “dfhttegd”.

STRRAT RE

Within this class and method we can see that STRRAT is performing an AES decryption routine based on the provided AES encrypted stream from config.txt, and a “password” of “strigoi”.

STRRAT RE

Although I’m no crypto expert, in short, the password is being used to derive a secret key via the “password-based key derivation function 2” (PBKDF2), 65536 SHA1 hashing iterations, a key size of 128, using CBC mode, and a salt which is the ‘nonce’ extracted from our AES encrypted string. In this instance the “salt” (nonce) functions as the Initialisation Vector used with the AES Key to decrypt the string.

  • More reading around AES Encryption and Decryption in Java can be found created by baeldung

To decrypt this using CyberChef we would have to first get the salt (nonce) used, derive the PBKDF2 key, and then use this with the appropriate IV to decrypt our content.

STRRAT RE

This is a bit of work, and can quickly get confusing. Because STRRAT is implementing a decryption method, we can go ahead and take the relevant parts of the decryption method to decrypt the config file ourselves. If we copy “sabretb” and “dfhttegd” from “sabretb”, and also “bsgshsbs” from Main, we’ve got the start of a decryption program (noting we will need to import the appropriate libraries in use).

STRRAT RE

In the above we’ve copied the core content in; however, there’s still some issues based on duplicate named variable declarations, classes which aren’t throwing an exception, and an issue based on how it is reading the file input. I’ve gone ahead and rectified this with the end result being a Decrypter which takes 2 arguments, (input file, output file). Note: At present during our analysis there’s some unknown configuration values as we haven’t completely reveresed the malware.

STRRAT RE

The decrypter as it stands so far can be found below:

import java.nio.ByteBuffer;
import java.io.File;
import java.nio.file.StandardOpenOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class STRRATConfigDecrypt {

    public static void main(String[] args) throws Exception {
        System.out.println("###################################################");
        System.out.println("# STRRAT Malware (v.1.4) Decrypter by @CyberRaiju #");
        System.out.println("###################################################");
        if (args.length < 1) {
            System.out.println();
            System.out.println("PEBCAK - Use this with 2 arguments, an input and output file");
            System.out.println("Example:");
            System.out.println(
                    "java -jar STRRATConfigDecrypt.jar \'C:\\Users\\User\\config.txt\' \'C:\\Users\\User\\decrypted_config.txt'");
        } else {
            String inputdirectory = args[0].replace("\\", "\\\\");
            String outputdirectory = args[1].replace("\\", "\\\\");
            File configfile = new File(inputdirectory);
            byte[] config = Files.readAllBytes(configfile.toPath());
            byte[] parsedBytes = Base64.getDecoder().decode(config);
            byte[] decryptedConfig = DecryptAES("strigoi", parsedBytes);
            String string[] = new String(decryptedConfig, StandardCharsets.UTF_8).split("\\|");
            System.out.println("Network IOC: " + string[0]);
            System.out.println("File Lock: " + string[1] + "lock.file");
            System.out.println("Network IOC: " + string[2]);
            System.out.println("Network IOC: " + string[3]);
            System.out.println("Unknown Parameter: " + string[4]);
            System.out.println("Unknown Parameter: " + string[5]);
            System.out.println("Unknown Parameter: " + string[6]);
            System.out.println("Unknown Parameter: " + string[7]);
            System.out.println("Possible License/Campaign ID: " + string[8]);
            Path output = Paths.get(outputdirectory);
            Files.write(output, "# STRRAT Malware (v.1.4) Decrypter by @CyberRaiju #\r\n".getBytes());
            for (String str : string) {
                str = str+"\r\n";
                Files.write(output, str.getBytes(),StandardOpenOption.APPEND);
            }
            
        }
    }

    public static SecretKey SecretKey(String password, byte[] data) throws Exception {
        PBEKeySpec a = new PBEKeySpec(password.toCharArray(), data, 65536, 128);
        byte[] b = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1").generateSecret(a).getEncoded();
        return new SecretKeySpec(b, "AES");
    }

    public static byte[] DecryptAES(String password, byte[] bytes) throws Exception {
        ByteBuffer byteBuffer;
        int n;
        byteBuffer = ByteBuffer.wrap(bytes);
        n = byteBuffer.getInt();
        if (n < 12 || n > 16) {
            throw new IllegalArgumentException(
                    "Nonce size is incorrect. Make sure that the incoming data is an AES encrypted file.");
        }
        byte[] object3 = new byte[n];
        byteBuffer.get(object3);
        SecretKey key = SecretKey(password, object3);
        byte[] byArray2 = new byte[byteBuffer.remaining()];
        bytes = byArray2;
        byteBuffer.get(byArray2);
        Cipher crypto = Cipher.getInstance("AES/CBC/PKCS5PADDING");
        IvParameterSpec IVSpec = new IvParameterSpec(object3);
        crypto.init(2, key, IVSpec);
        return crypto.doFinal(bytes);
    }
}

After ensuring the config file has been extracted, by running this using Visual Studio Code and the following command line:

cd 'c:\Users\User\Desktop\STRRAT\Stage 2\STRRAT-Deobfuscated\carLambo\resources\Decrypter\STRRATConfigDecrypt'; & 'c:\Users\User\.vscode\extensions\vscjava.vscode-java-debug-0.31.0\scripts\launcher.bat' 'C:\Program Files\OpenJDK\openjdk-11.0.9_11\bin\java.exe' '-Dfile.encoding=UTF-8' '-cp' 'C:\Users\User\AppData\Roaming\Code\User\workspaceStorage\ed67bcda7ebbfaeaf8f2ea3a04d08723\redhat.java\jdt_ws\STRRATConfigDecrypt_7776d08d\bin' 'STRRATConfigDecrypt' "C:\Users\User\Desktop\STRRAT\Stage 2\STRRAT-Deobfuscated\carLambo\resources\config.txt" "C:\Users\User\Desktop\STRRAT\Stage 2\STRRAT-Deobfuscated\carLambo\resources\decrypted.txt"

We’re presented with a decrypted configuration output which has also been stored in a file called “decrypted.txt”.

STRRAT RE

Network IOC: moregrace[.]duckdns[.]org
Host IOC: 3219lock.file
Network IOC: hxxp[://]jbfrost[.]live/strigoi/server/?hwid=1&lid=m&ht=5
Network IOC: palaintermine[.]duckdns[.]org

Although the config parameters have been extracted, it’s not clear on exactly what they’re used for. By looking back into the method “sdfsldf” in the Main class we can see it is checking arguments provided, and if an argument was provided, it would only use the 3rd configuration parameter from the file (the jbfrost[.]live URL) in a call to “cbnfdhn.sabretb”.

STRRAT RE

In addition to this, the above mentioned method is all about making a GET request to the server and retrieving the response to return as a string.

STRRAT RE

We know that this string is being returned to cbnfdhn.dfhttegd which if we look into is holding a public string that can be referenced from multiple classes, so we know it is being stored for later usage.

STRRAT RE

By viewing references to this, we can see the different methods which either “PUT” a value into this variable, or “GET” the value from the variable.

STRRAT RE

At this point we know that the variable is written to within 5 methods, and read from 5 methods. If we examine where it is being read from by right clicking and selecting “Edit with Assembler”, we can see that all of these methods are creating sockets and using this as the callback address. In addition we can see reference to what appears to be “Reverse Proxy” and “HDRDP” debug/response messages.

STRRAT RE

By performing some OSINT using urlscan.io we find that this URL used to just return ‘pluginserver[.]duckdns[.]org’, which is the same domain we found linked to our pastebin URL.

STRRAT RE

It’s very likely that this domain was being used to fetch plugins for STRRAT. To tie most of this back together we can return to method “sdfsldf” within the Main class and examine more closely what it is doing here.

STRRAT RE

In the above we see it is defining a couple of variables, one for the primary plugins URL which is extracted from the config file, and one which is pointing to the pastebin URL containing the same domain. We then see it is using this to download the appropriate Java Archive files (plugins), before a request is made to a master server which looks to have been registered to take a license ID. In this case the license ID would be “khonsari”.

We can also confirm that after this a file lock is being created in the user home directory by examining the object defined within object9 which in this case is an instance of “dfghrth”.

STRRAT RE

At this stage we can begin to update our decrypter to refine what we’re pulling in from the config file.

STRRAT RE

It should be noted that we’ve inferred this is STRRAT version 1.4 by examining the method “shshnfdn” within “cbnfdhn” which looks to be a check to see if STRRAT has been installed on this host or not.

STRRAT RE

If we move back to examining the main method which will continue to run, we can see that one of the parameters passed as “True” is used to determine if persistence will be setup using a scheduled task called “Skype”, with another being used to determine if the configuration will be passed to a new array. This new array is then used within sstydgn.sdfsldf, where the stringArray3 7th parameter is also set to true.

Host IOC: Scheduled Task - "Skype"

STRRAT RE

If we once again examine references to this, it appears as if these values are also being used to determine if persistence is installed, only this time it is writing to the user’s startup directory.

STRRAT RE

At this stage we have enough information and context to complete our STRRAT configuration decrypter and can compile a Java Archive to extract the configuration from a given config.txt file.

"C:\Program Files\OpenJDK\openjdk-11.0.9_11\bin\java.exe" -jar STRRATConfigDecrypt.jar "C:\Users\User\Desktop\STRRAT\Stage 2\STRRAT-Deobfuscated\carLambo\resources\config.txt" "C:\Users\User\Desktop\STRRAT\Stage 2\STRRAT-Deobfuscated\carLambo\resources\decrypted.txt"

STRRAT RE

The completed STRRAT Config Decrypter has been made available for use here. This takes a configuration file and decrypt’s it.

Alternatively, after collaborating with some bright minds on Twitter (shoutout to Chaitanya - @CbGmanit who reached out with assistance on properly getting the salt (nonce) used), the following recipe has been devised to fully decrypt this using CyberChef here

From_Base64('A-Za-z0-9+/=',true)
To_Hex('Space',0)
Register('([\\s\\S]{11}).([\\s\\S]{47})',true,false,false)
Register('([\\s\\S]*)',true,false,false)
Derive_PBKDF2_key({'option':'UTF8','string':'strigoi'},128,65536,'SHA1',{'option':'Hex','string':'$R1'})
Register('([\\s\\S]*)',true,false,false)
Find_/_Replace({'option':'Regex','string':'.*'},'$R2',false,false,true,false)
Find_/_Replace({'option':'Regex','string':'^([\\s\\S]{60})'},'',true,false,true,false)
AES_Decrypt({'option':'Hex','string':'$R3'},{'option':'Hex','string':'$R1'},'CBC','Hex','Raw',{'option':'Hex','string':''},{'option':'Hex','string':''})
Find_/_Replace({'option':'Regex','string':''},'',true,false,true,false)
Register('([a-zA-Z.]*).([a-zA-Z0-9.]*).([a-zA-Z0-9\\:?@=\\-&%//.]*).([a-zA-Z.]*).([a-zA-Z0-9.]*).([a-zA-Z0-9.]*).([a-zA-Z0-9.]*).([a-zA-Z0-9.]*).([a-zA-Z0-9\\:?@=\\-&%//.]*)',true,false,false)
Find_/_Replace({'option':'Regex','string':'.*'},'$R4',false,false,true,false)
Find_/_Replace({'option':'Simple string','string':'$R4'},'C2: $R4',false,false,true,false)
Register('([\\s\\S]*)',true,false,false)
Find_/_Replace({'option':'Regex','string':'.*'},'$R5',false,false,true,false)
Find_/_Replace({'option':'Simple string','string':'$R5'},'Primary Lock/Port: $R5',false,false,true,false)
Register('([\\s\\S]*)',true,false,false)
Find_/_Replace({'option':'Regex','string':'.*'},'$R6',false,false,true,false)
Find_/_Replace({'option':'Simple string','string':'$R6'},'Plugins Download URL: $R6',false,false,true,false)
Register('([\\s\\S]*)',true,false,false)
Find_/_Replace({'option':'Regex','string':'.*'},'$R7',false,false,true,false)
Find_/_Replace({'option':'Simple string','string':'$R7'},'Secondary C2: $R7',false,false,true,false)
Register('([\\s\\S]*)',true,false,false)
Find_/_Replace({'option':'Regex','string':'.*'},'$R8',false,false,true,false)
Find_/_Replace({'option':'Simple string','string':'$R8'},'Secondary Lock/Port: $R8',false,false,true,false)
Register('([\\s\\S]*)',true,false,false)
Find_/_Replace({'option':'Regex','string':'.*'},'$R9',false,false,true,false)
Find_/_Replace({'option':'Simple string','string':'$R9'},'Startup Folder Persistence: $R9',false,false,true,false)
Register('([\\s\\S]*)',true,false,false)
Find_/_Replace({'option':'Regex','string':'.*'},'$R10',false,false,true,false)
Find_/_Replace({'option':'Simple string','string':'$R10'},'Secondary Startup Folder Persistence: $R10',false,false,true,false)
Register('([\\s\\S]*)',true,false,false)
Find_/_Replace({'option':'Regex','string':'.*'},'$R11',false,false,true,false)
Find_/_Replace({'option':'Simple string','string':'$R11'},'Skype Scheduled Task Persistence: $R11',false,false,true,false)
Register('([\\s\\S]*)',true,false,false)
Find_/_Replace({'option':'Regex','string':'.*'},'$R12',false,false,true,false)
Find_/_Replace({'option':'Simple string','string':'$R12'},'License ID: $R12',false,false,true,false)
Register('([\\s\\S]*)',true,false,false)
Find_/_Replace({'option':'Regex','string':'.*'},'$R13\\n$R14\\n$R15\\n$R16\\n$R17\\n$R18\\n$R19\\n$R20\\n$R21',false,false,true,false)

Simply put in the config file above, and receive your decrypted configuration file.

Update: 6/4/2022 - This has also been ported to Python3 and improved upon for those who like to incorporate this into their automation pipelines, or who just enjoy some ASCII artwork ;) and can be found here. This takes a jar file (strrat) which contains the configuration file within its resources and automatically reads and decrypts this without the need to extract it first.

Usage: python3 decrypt-strrat.py [PATH TO STRRAT]

Part 3: Examining RAT Flow and Functionality

To drill into this a bit more, we need to know what Java classes are going to be most interesting. One way of doing this is to extract the classes and filter based on their size, because a larger class may often be indicative of more code making up more interesting functionality.

STRRAT RE

The easiest way to view this code is to view it in Decompiler view rather than viewing them in the ‘Edit with Assembler’ view we used above.

STRRAT RE

If we’re having issues with using the Decompiler, it can often be useful to revisit previous steps. For example if we’re unable to decompile a class, perhaps we should try to deobfuscate the malware using the second transformer recommended by java-deobfuscator, or we could try a different decompiler within Recaf.

In this instance there doesn’t appear to be any issues, so let’s first look back at the references to our public static string object ‘cbnfdhn.dfhttegd’ which is essentially holding the value “pluginserver[.]duckdns[.]org”. Within the fgfnbnc class we find this is referenced 3 times. In the first instance we see it is the method cbnfdhn.dfhttegd being run rather than the public string being accessed.

STRRAT RE

Because this is a method, we disregard this hit, but make note that whenever the method fgfnbnc.dfhttegd is run, it will attempt to run the method cbnfdhn.dfhttegd. Looking at the next method we can see the reference to HRDP-MGR we saw previously with a little more context.

STRRAT RE

In this instance we can see a new registry key being created which ensures that User Names are not shown at the login screen.

Host IOC: HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System /v dontdisplaylastusername = 1

This is interesting as it immediately follows what looks to be ensuring that Terminal Services are enabled, and a call to add a user account. To determine if there’s a hardcoded user account we can look out for we will need to examine ‘object2’ which is being cast to a string here; however, before we do we’ll begin to rename some of these variables to help keep track of what we’re looking at. To do this we will need to ensure we’ve highlighted the correct class reference, and right click > rename. Using Recafe all associated class references will be updated.

STRRAT RE

In this instance I’ve renamed it to PluginsServerURL, and also renamed the other reference in this socket connection to PluginsServerPort. Moving back to ‘object2’, we can see this is being assigned by fgfnbnc3.dfhttegd. By viewing the definition of this variable, we find it is defined in the same class we’re currently in.

STRRAT RE

Renaming this variable and revisiting the call, we see that there’s an object definition of fgfnbnc3 from the class fgfnbnc which is why we didn’t see a class with the name fgfnbnc3.

STRRAT RE

The benefit of renaming the String is that now we can find all references to that easily. If we continue to examine this class we find that there’s a definition of this variable within fgfnbnc’s fgsbdfgsb method.

STRRAT RE

Examining this method reveals it is a small function used to build a random string to be used as a Username.

STRRAT RE

It’s important to remember that because we’ve managed to get some decompiled code, string builders like this can be replicated using VS Code to confirm what we’re reading. Although this is a basic method, we can still recreate it by making a class that spits out the output of this method.

STRRAT RE

From this we can see this will create a random string with 5 characters.

STRRAT RE

To get some structure behind analysing this malware flow, we’ll return to the main class that we know will be run as soon as this malware kicks off. First we’ll begin to rename any methods or variables we’ve found so far and give them more descriptive names based on their perceived functionality. I won’t walk through all of the analysis before renaming the variables to assist with analysing the malware in a more meaningful way. We’ll continue to rename more as we determine their purpose; however to start off the following have been renamed:

  • cbnfdhn.sabretb (Method) = cbnfdhn.MakeGetRetResponse
  • Main.sdfsldf (Method) = Main.ObjMakeGetRetResponse
  • Main.sdfsldf (Static String) = Main.STRPluginLocations
  • Main.sabretb (Static String) = Main.STRFileSeparate
  • Main.bsgshsbs (Object) = Main.ObjFileLock1
  • Main.dfhttegd (Object) = Main.ObjFileLock2
  • dfghrth (Class) = FileLock
  • Main.sdfsldf (Method, no input) = Main.GetJarRunPath
  • cbnfdhn.sdfsldf (Method, inputs) = cbnfdhn.C2ConnectAndRun
  • cbnfdhn.mdgghtdsh (Static InputStream) = cbnfdhn.InStream1
  • cbnfdhn.sgsfghhg (Static OutputStream) = cbnfdhn.OutStream1
  • cbnfdhn.hghteerd (Method, no input) = cbnfdhn.ConnectToC2
  • cbnfdhn.gsbthstgb (Static String) = cbnfdhn.Port1
  • xbxcv (Class) = UserIdleObject
  • Main.bsgshsbs (Method, no inputs) = Main.ExtractConfig
  • sabretb.dfhttegd (Method, inputs) = sabretb.DecryptConfig
  • sabretb.sdfsldf (Method, inputs) = sabretb.DeriveIV
  • sabretb.sabretb (Method, inputs) = sabretb.CreateSecKey
  • Main.dfhttegd (Method, no inputs) = Main.ConnectUnknownURL
  • cbnfdhn.sdfsldf (Method, one int input) = cbnfdhn.GetFolderPath
  • cbnfdhn.sdfsldf (Method, one string array input) = cbnfdhn.Uninstall
  • cbnfdhn.bsgshsbs (Static String) = cbnfdhn.FolderPath
  • cbnfdhn.sfsrgsbd (Static String) = cbnfdhn.STRLogsLocation
  • cbnfdhn.sabretb (Static String) = cbnfdhn.STRBackupC2
  • cbnfdhn.sdfsldf (Static Boolean) = cbnfdhn.ActiveConToC2
  • cbnfdhn.thtyrths (Method, no input) = cbnfdhn.ParseNumOfCommands
  • cbnfdhn.sabretb (Method, one int input) = cbnfdhn.ReceiveCommands

If we now examine the main method again we find the below:

STRRAT RE

If the malware is able to get the current malware run path and place dependency files in a folder called ‘lib’, it will then attempt to get the AppData directory based on CSIDL 26. The malware will continue to extract/decrypt its config file and make requests to get any plugins from the URL configured. Finally after this we see it connect to the C2 ready to execute commands received. It should be noted that our defined variables PluginsServerURL and PluginsServerPort are both being reused here as the C2 configuration from within the decrypted config file.

STRRAT RE

In the above we see the start of our C2ConnectAndRun method. In this instance it will check if a directory already exists called strlogs in the malware working directory, if not it will create one. It will also create an object containing information on whether the infected user is idle, and how long they’ve been idle for, before it inevitably connects to the C2.

STRRAT RE

The ConnectToC2 method is pretty straight forward, based on the decrypted configuration file it will first close off any sockets if they already exist with the C2, next it will attempt to connect to the C2 server, and where that fails it will fallback to its backup C2.

STRRAT RE

At this point we can begin to see the different commands taken by this malware as they’re received into the array ‘stringArray2’ from the designated C2, which is also placed into stringArray3.

STRRAT RE

STRRAT RE

STRRAT RE

Based on this we know that STRRAT 1.4 has the following commands available.

  • reboot
  • shutdown
  • uninstall
  • disconnect
  • down-n-exec
  • update
  • up-n-exec
  • remote-cmd
  • power-shell
  • file-manager
  • keylogger
  • o-keylogger
  • processes
  • h-browser
  • startup-list
  • remote-screen
  • rev-proxy
  • hrdp-new
  • hrdp-res
  • chrome-pass
  • foxmail-pass
  • outlook-pass
  • fox-pass
  • tb-pass
  • ie-pass
  • all-pass
  • save-all-pass
  • chk-priv
  • req-priv
  • rw-encrypt
  • rw-decrypt
  • show-msg
  • screen-on

Let’s examine these commands more in depth:

Command Functionality: reboot

This command simply runs the following:

Runtime.getRuntime().exec("cmd.exe /c shutdown /r /t 0");

Command Functionality: shutdown

This command simply runs the following:

Runtime.getRuntime().exec("cmd.exe /c shutdown /s /t 0");

Command Functionality: uninstall

This command runs the following (Noting we’ve renamed the method to “Uninstall”):

cbnfdhn.Uninstall(stringArray);

The uninstall method will then proceed to delete the scheduled task persistence called skype regardless of if it exists, get the directory the malware is running from, delete it from the directory specified by CLSID 7 and 24 Listings can be found here, ping localhost to delay execution for a moment, and then delete the object passed to it, before what appears to be deletion of run keys.

STRRAT RE

It should be noted that the CLSID values are being passed as an integer (Decimal) rather than Hex and would need to be converted to get that 26 = AppData, and 7 = Users Startup Folder.

Command Functionality: disconnect

This command will set a boolean variable we’ve renamed as ActiveConToC2 to false, before closing any stream inputs that are being used in ertdbdth to log key pushes, and then calls exit for this process thus terminating the running STRRAT instance.

STRRAT RE

STRRAT RE

In essence the disconnect method is terminating the process and disconnecting the C2 connection to STRRAT.

Command Functionality: down-n-exec

This command will call 2 more functions inside of cbnfdhn, sabretb (object, string), and dfhttegd (string) which we’ll take a closer look at so they can be renamed.

STRRAT RE

The first passed parameter to sabretb is an object that will be containing a url from which to retrieve a remote file from. This takes the received file and stores it in object3 which is then launched using either java.exe, wscript.exe, or cmd.exe depending on its extension.

STRRAT RE

From here we can rename this sabretb method DownloadAndExecute. Finally examining dfhttegd (string) we can see this is just used to update the C2 on the STRRAT status, so we can name this and sdfsldf (string) StatusUpdate and StatusUpdateSend.

STRRAT RE

STRRAT RE

Command Functionality: update

This command will create a new object as a file passed to it by running cbnfdhn.gsbthstgb, release the current fileLock with a method called sdfsldf, execute the object that was passed down to the malware, and then uninstall the current STRRAT malware.

STRRAT RE

STRRAT RE

This seems to be a fairly hacked together (pun intended) update mechanism to send an updated version of STRRAT to infect the system, and remove the existing version. There’s not a lot of error handling to ensure that this is successful though, so if the update fails to execute the new version of STRRAT or the wrong number of parameters is sent, this can lead to STRRAT uninstalling itself without actually updating.

We will rename cbnfdhn.gsbthstgb to cbnfdhn.CreateFileObj for further analysis

Command Functionality: up-n-exec

This command will function similar to down-n-exec, only instead of it retrieving a file from a defined URL to run (download and execute) this is going to take an uploaded file directly from the C2 and execute it (upload and execute)

STRRAT RE

Command Functionality: remote-cmd

This command has 2 primary components, a new object creation with cbnfdhn.sfsrgsbd (this is very similar to sdfsldf (string) we renamed to StatusUpdateSend, except it does so using a new socket that’s created to the C2), and a new instance of dgdfndnbcn which is created using the newly created socket, and a string array containing “cmd.exe”.

STRRAT RE

We’ll rename cbnfdhn.sfsrgsbd to cbnfdhn.StatusUpdateSendNewSock. Analysing the new instance of dgdfndnbcn, we can see that this is about 150 lines of decompiled code long, but we can start with the definition using a socket object and a string array.

STRRAT RE

Although the obfuscation here makes things a little bit confusing, if we look at what we’re dealing with as a whole, we can see there’s an input and output stream from the C2, a new process using ProcessBuilder, and new threads that will be started for a given process.

What’s interesting with this is that only a socket and cmd.exe is being passed to this process creation, and there’s no command-line parameters given. The reason for this is because it creates a new instance of sabretb, which in this case is the process being run, and is redirecting input and output from cmd.exe directly to the established socket connection to the C2.

STRRAT RE

If an error occurs a boolean is switched and the socket is released. To this end, remote_cmd is a way of issuing remote commands to the endpoint in the form of a reverse shell.

We can rename dgdfndnbcn to ReverseShell for ease of analysis.

Command Functionality: power-shell

This command functions much the same as remote_cmd, only it is executing an instance of powershell.exe with the command-line “-“ as opposed to running cmd.exe, which I assume is to support command-line arguments with PowerShell.

STRRAT RE

Command Functionality: file-manager

This command first creates connects to a new Socket on the C2 and sends a status update that the file-manager has been opened before creating a new instance of ‘ghdfdndfn’ using the created socket as a passed parameter.

STRRAT RE

By examining ‘ghdfdndfn’ we can see that a new object reference to itself is defined as ghdfdndfn2, before a variable of sdfsldf is set to the passed Socket, dfhttegd is set as the input stream to this socket, and sabretb is set as the output stream to this socket.

STRRAT RE

The main functionality of this object is stored within the class dfhttegd where commands can be sent via the established socket connection once the file-manager command has been run.

STRRAT RE

The commands that can be sent are:

  • navigate
  • nav-key-log
  • open
  • delete
  • savefile
  • bringfile

If navigate is sent, a new call is made to dfhttegd, passing a string that is returned from a call to sabretb which takes a passed string as a parameter. To figure out what is happening, we need to take a look at sabretb.

STRRAT RE

Taking a provided string, sabretb creates an array of files (directories included) based on the given string and then formats the returned file information for ease of visibility for each given file in the array. In addition this will check if the file is a directory, and if so it will insert a ‘D’ into the returned response. If not it will insert a ‘F’, and perform calculations of the file size by dividing of 1024 to get the number of kilobytes for each file returned.

This means that “navigate” effectively functions as a command to “view inside this directory and show me the contents”.

“nav-key-log” performs the same call; however, instead of passing a string provided by the C2, it will automatically pass, cbnfdhn.STRLogsLocation which is the location of STRRAT logs we’ve previously renamed, and thus we can infer this command is to navigate to the log directory and display contents.

open

If “open” is sent, STRRAT takes this as an instruction to execute the file using cmd.exe which will use the default COM object for the particular file extension it is attempting to run.

Runtime.getRuntime().exec(new StringBuilder().insert(0, "cmd.exe /c \"").append(stringArray[1]).append("\"").toString());

delete

If “delete” is sent, STRRAT makes a call to sdfsldf (file) passing into it the output of a new file object being created with a given string passed to it. By examining sdfsldf we can see that this is a simple function designed to first determine if the object referenced by the string passed is a directory, if so then it will recursively delete all the files inside of this, and the directoy. If not, it will simply delete the file passed to it.

STRRAT RE

savefile

If “savefile” is sent, STRRAT makes a call to sdfsldf (String) passing in a given file which is read into a FileOutputStream to allow it to be written to disk.

STRRAT RE

bringfile

Finally, if “bringfile” is sent, STRRAT creates a new file object and checks if the file object (handle) it now has is a file or not, if not an error is sent back stating that “You cannot download a folder”. Where it is a file, it will make a call to sabretb (file) passing it in this file.

STRRAT RE

sabretb (file) will send the bytes of the given file back through to a FileInputStream allowing the file to be downloaded.

STRRAT RE

From the above scenarios, although we’re not sure if these commands are sent manually or by an interactive file-manager hosted by the C2, we can conclude that “file-manager” provides a virtual file manager which allows the above functionality.

Command Functionality: keylogger

This command focuses primarily around the class ghgmgf. If ghgmgf.sabretb is not true, this will create a new object from the class ghgmgf, if it is true it will be set to false.

STRRAT RE

By examining ghgmgf we see that sabretb is a simple static boolean being referenced. Further to this we can examine the primary ghgmgf method to get an idea of what is happening.

STRRAT RE

Taking into account the existing socket connection which is passed into this method, there’s also a null string being passed when creating the object, and due to this an entry at line 101 isn’t triggered until after a new ghgmgf object has been instantiated and started. Examining line 101 reveals HTML text which is being passed to ‘bsgshsbs’ for writing, which includes the terms ‘Keyboard Log’ and ‘Generated by Strigoi Master’

STRRAT RE

This is very basic HTML and winds up looking similar to the below, presumably appended with Keylogs as keystrokes are logged, and it tells us that the author of this malware possibly favors a dark theme.

STRRAT RE

It should be noted that if any failures occur an instance of ‘gsbthstgb()’ is called which looks to be closing any existing sockets and logging that a Keylogger has been shutdown.

STRRAT RE

To understand what this command aims to do we need to look at the below line which runs when a new object of ghgmgf is created after the null check above is performed.

new Thread(new xnxcxvb((ghgmgf)object)).start();

By decompiling ‘xnxcxvb’ we can see that it implements the Runnable class with an instance of ghgmgf it is being passed that is assigned to ‘sdfsldf’.

STRRAT RE

The key component here is that when a new thread is being created it will run ‘ghgmgf.sabretb(this.sdfsldf)’, which means we can take a look at the method inside ‘sabretb(ghgmgf)’ to determine what will happen.

STRRAT RE

It’s within this method that a GlobalKeyboardHook is registered, which we know is used by the System Hook Plugin that STRRAT relies on. By decompiling this class we can see a number of keycodes being registered for listening, and a number of combination of keys to ensure that these are also logged.

STRRAT RE

STRRAT RE

Given this context and because we previously saw the below line within ‘ghgmgf’

this.sfsrgsbd = ((Socket)object).getOutputStream();

We can infer that all keys logged are sent directly back to the established socket connection to the C2 in realtime, and that the command ‘keylogger’ functions as expected as a keylogger leveraging the low level library hook freely available.

Command Functionality: o-keylogger

This command uses the same method as ‘keylogger’, only it instead passes in a string specifying the location to log keys to disk instead of a socket. This file is a HTML file that takes the name “keylogs_.html" and is stored in our previously discovered "STRLogsLocation".

STRRAT RE

From this we can infer that o-keylogger starts an offline keylogger which writes its output to disk rather than sending over an established socket.

Command Functionality: processes

This command creates a new object of class ‘sbsgssdfg’, passing into it the existing socket connection.

STRRAT RE

Examining ‘sbsgssdfg’ we find that this implements another class that uses the Runnable class ‘sbsbgsrg’, which if we examine this can see that upon running it will execute the ‘sdfsldf (sbsgssdfg)’ method.

STRRAT RE

STRRAT RE

Examining this method we can see that it is looking for parameters to have been passed, as either ‘reload’ or ‘kill’.

STRRAT RE

Where ‘reload’ is passed, it will run an instance of sdfsldf(), and where ‘kill’ is passed, it will use the inbuilt Windows ‘taskkill’ command to kill a process based on its PID passed to the kill command. Examining sdfsldf(), we can see that this leverages the inbuilt Windows ‘wmic’ to enumerate process information.

STRRAT RE

From this we can infer that ‘processes’ is used to interact with running processes on a system to either enumerate and show them, or kill one specified by its PID.

Command Functionality: h-browser

This command primarily functions from within the ‘sgsfghhg’ class.

STRRAT RE

This leverages the ‘HBrowserNativeApis’ class to return appropriate bitmap representations for specific browser windows running on the victim machine.

STRRAT RE

STRRAT RE

There’s a lot of code in amongst here for drawing and sending over a socket the visual contents of essentially a ‘virtual web browser’ to the adversary so we’ll hone in on some specific code. The first thing we can notice inside ‘sgsfghhg’ is the presence of a number of commands being sent down the socket including ‘start’, ‘stop’, ‘mouse-event’, and ‘key-event’.

STRRAT RE

Within the above we also see that when ‘start’ is sent we get another new thread spawning for an object ‘fdghdmh’.

new Thread(new fdghdmh(sgsfghhg2, (String)object3)).start();

This gives us a new method which will be run when this command is sent down which is ‘sgsfghhg.sdfsldf(sgsfghhg, String)’.

STRRAT RE

By examining this method we can see this is largely based around creating a byte array that’s written down the Socket in the form of a rendered PNG.

STRRAT RE

STRRAT RE

Although in this instance there’s some broken decompiled code, we can see many instances of this referencing chrome.exe and firefox.exe within methods including sdfsldf().

STRRAT RE

and sabretb().

STRRAT RE

Finally if we cross reference any references to these we can see that a large proportion of this is facilitated in the method ‘dfhttegd(String)’ which contains references to a ‘Strigoi Browser’ which starts as either a chrome or firefox process depending on what is on the victim computer.

STRRAT RE

From this we can infer that ‘h-browser’ is used to spawn a virtual ‘HTML Browser’ dubbed ‘Strigoi Browser’ using either Firefox or Chrome as the host process for this.

Command Functionality: startup-list

This command creates a new object of class ‘sstydgn’, passing into it the existing socket connection.

STRRAT RE

Once again we see a familiar thread creation, this time defined within the class ‘ncgdfhbn’.

STRRAT RE

Once again implementing the runnable class this is set to kick off ‘sstydgn.sdfsldf(ssydgn)’.

STRRAT RE

From here we see this takes one of 3 commands, ‘reload’, ‘delete’, or ‘add’.

STRRAT RE

reload

When ‘reload’ is sent this command will proceed to run an instance of ‘sstydgn.sdfsldf()’. This will first run ‘wmic’ to enumerate current notable autostart entries on the system. When accounting for escape characters the command run is as follows.

wmic /node:. /namespace:'\\root\cimv2' path win32_startupcommand get name,location /format:list

This then performs data processing based on the way results are returned. An example of a returned entry is included below:

Location=HKU\S-1-5-19\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
Name=OneDriveSetup

If we look at how this manifests in the code we see the following:

STRRAT RE

From the above and example provided we can see this is splitting the results of the location and name results in what it is presenting based on the returned ‘=’. We can also see evidence that this is translating entries that simply say ‘Startup’ to the CSIDL ordinal (Folder path) ‘7’ (CSIDL_STARTUP), ‘Common Startup’ to the CSIDL ordinal (Folder path) ‘24’ (CSIDL_COMMON_STARTUP), ‘HKU’ to the full string ‘HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run’, and ‘HKLM’ to the full string ‘HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run’.

Note: More information on CSIDL Ordinals

The issue with the above approach is that it contains a logic bug. It assumes that every result returned will be that of the current user (HKCU) - HKEY Current User; however, when running this WMI command it will return results from other user SIDs such as ‘.DEFAULT’, ‘S-1-5-18’ (NT AUTHORITY\SYSTEM), and in the case of running as Admin, other users on the system. Because of this the results returned will state the run keys are always that of the current user. Further to this it will also replace the key with the location of the standard ‘Run’ key; however, if this was instead a different key such as ‘RunOnce’, it would still resolve as the ‘Run’ key location because of this logic flaw. This demonstrates an oversight by the malware author.

From this we can infer that ‘startup-list reload’ is used to show the current startup (autorun) items on this host.

delete

When ‘delete’ is sent this command will proceed to run an instance of ‘sstydgn.sdfsldf(String)’. This will check what is being passed to it, and where it starts with either HKCU or HKLM it will attempt to delete it from registry using the native ‘reg.exe’ command. Where this doesn’t start with either of these characters the malware will attempt to delete it as if it was a file or folder on the system.

STRRAT RE

This demonstrates another oversight where the malware only has support for deleting startup keys with this for the current user or local machine, and not other user hives.

From this we can infer that ‘startup-list delete’ is used to remove either the current user/local machine registry run key, or a file present on the system.

add

When ‘add’ is sent this command will proceed to run an instance of ‘sstydgn.sdfsldf(String, boolean, boolean)’. This functions as a way to add persistence through both a registry run key, or a file written to either CSIDL_STARTUP or CSIDL_COMMON_STARTUP.

STRRAT RE

This uses another method ‘sfsrgsbd.sdfsldf(int, String)’ to perform the registry additions. What’s interesting here is that rather than using ‘reg add’ native commands as was done with the ‘delete’ command, this instead uses methods defined in Java that leverage relevant Windows APIs.

STRRAT RE

STRRAT RE

This difference highlights one of maybe 3 possible scenarios.

  • The malware author intentionally chose to not leverage reg.exe to add in persistence to evade some EDR signatures.
  • The malware author is actually instead malware authors, all of whom focus on a different part of the RAT, or it has been adapted from code “borrowed” or “stolen” over time.
  • The malware author didn’t realise that there was an ‘add’ parameter for reg.exe.

Given the code quality inconsistencies I’d say it’s likely this was made by multiple people, or it has been chopped and changed/stolen over time.

From this we can infer that ‘startup-list add’ is used to add either an entry to the current user/local machine registry run key, or a file to one of 2 known startup directories on the system.

Command Functionality: remote-screen

This command primarily functions from within the ‘sabretb(Socket)’ class.

STRRAT RE

At a glance we can see that this is importing a lot of classes to do with encryption and drawing pixels to the screen.

STRRAT RE

Note: To get a clean decompilation we’ll be switching to the FernFlower decompiler built into Recaf.

Taking a look at the main class executed see 2 familiar thread creations in addition to some input and output stream declarations. This time the thread creations are defined within the classes ‘fgsbsfgsb’ and ‘hghteerd’.

STRRAT RE

Class ‘fgsbsfgsb’ will be ensuring ‘sdfsldf(sabretb)’ is run.

public final void run() {
    sabretb.sdfsldf(this.sdfsldf);
}

Class ‘hghteerd’ will be ensuring ‘sabretb(sabretb)’ is run.

public final void run() {
    sabretb.sabretb(this.sdfsldf);
}

If we compare these classes we see that ‘sdfsldf(sabretb)’ is expecting a successfully established socket so that it can send bytes which are stored from an instance of sabretb.sdfsldf(BufferedImage). We can also see reference to Remote Desktop in a debug message.

STRRAT RE

Examining this method we can see that it looks to be sending a ByteArray Stream of the user’s screen down the socket in the form of a continuous JPG.

STRRAT RE

Now looking at ‘sabretb(sabretb)’ we see that this is looking for events indicating whether a mouse has been moved, key pushed, mouse wheel scrolled, or mouse double clicked.

STRRAT RE

These events directly cause events that are directly tied to updating the image being sent down the established socket and sends commands through the java.awt.Robot class that has been imported here.

From this we can infer that ‘remote-screen’ is used to spawn a ‘VNC’ or ‘RDP’ type connection to the system and allow control of the mouse and keyboard input.

Command Functionality: rev-proxy

This command primarily functions from within the ‘ncnndfg(String)’ class.

STRRAT RE

Examining this we can see that this looks to be establishing a new Socket connection to the configured ‘PluginsServerURL’ and ‘PluginsServerPort’ variables. From this we can begin to see this appears to function also as a facilitator for this ‘rev-proxy’ command.

STRRAT RE

Some key components with this stem from the definition of ncnndfg3.sdfsldf as a new ‘fgssdg’ object, passing in the ‘ncnndfg’ object itself, before running this object, passing in a socket connection, and the object created (confusing I know).

ncnndfg3.sdfsldf = new fgssdg(this);
ncnndfg3.sdfsldf((Socket)object, ncnndfg3.sdfsldf);

Breaking down the crux of this we need to look at the ‘fgssdg’ class, and we can see stark similarities with the ‘ncnndfg(String)’ class.

STRRAT RE

The difference here is that instead of the above mentioned implemenetations this is running ‘ncnndfg.sabretb(ncnndfg, Socket, xbvcxnx)’, and examining this we find yet another invocation, this time of ‘ncnndfg2.sdfsldf(socket)’

STRRAT RE

Remaining cognizant that this is passing in an established socket and interface ‘xbvcxnx’ which references ‘sdfsldf()’, we finally can see this starts a new thread implementing ‘ngdnbn(ncnndfg, Socket, xbvcxnx)’ which we’re now well and truly familiar with thread runnable objects.

STRRAT RE

STRRAT RE

Pivoting back to ncnndfg we can now begin to see the main part of this command’s functionality.

STRRAT RE

STRRAT RE

Some important parts of this method occurs at lines 104-114 which is involved in receiving a CONNECT network request and sending back a 200 Connection Established message. This is only possible due to an instance of ncnndfg (ncnndfg2) being defined as object3 at line 76.

STRRAT RE

Here we also see that when a socket is established a new instance of ‘ghsghnbn’ will be created at line 112 whilst invoking a public method ‘sdfsldf(Socket, Socket)’. Examining what this method entails we can see it is largely around defining 2 socket connections passed in and then creating 2 new threads, one for the object ‘mdgghtdsh’ and the other, ‘ndgdfhfh’.

STRRAT RE

Taking a look at what these are used for, both once again are starting a thread using the Runnable class, and one will execute the method ‘sdfsldf(ghsghnbn)’, whilst the other executes ‘sabretb(ghsghnbn)’, both inside of ‘ghsghnbn’. Taking a look closer we can see that both of these are very similar except with some subtle differences.

STRRAT RE

In the above we can see that the difference occurs with the input and output streams being used, and these are the exact same streams defined for each socket passed for ‘sdfsldf(Socket, Socket)’ as shown previously.

There’s really not a lot of further investigation or code to unravel in this command, and from this we can infer that ‘rev-proxy’ is used to create a ‘reverse proxy’ or socks proxy on the host that would allow accessing an internal service/port on the host by hitting an external domain on the correct port (in this case the plugins server).

Command Functionality: hrdp-new

This command primarily functions from within the ‘fgfnbnc(String)’ class. We’ve touched on some of the elements of this previously, but now we’ll go into a bit more depth. Decompiling once again using FernFlower we can see that this gets reference to a hrdpinst.exe binary that will be stored within the Jar run path once downloaded, before noting the appropriate browser in use on the system (x86 or x64 version of Firefox or Chrome), and starting a new thread of ‘gmgmgmgm(fgfnbnc)’.

STRRAT RE

Examining this we see ‘sdfsldf(fgfnbnc, String)’ being called which in turn runs ‘cbnfdhn.StatusUpdate(“Initializing HRDP”)’ to update the C2. In addition this will run ‘sdfsldf(fgfnbnc)’ to ensure that HRDP has been downloaded and initialised, and that a new user account has been created that can be used with this tool.

STRRAT RE

The specific downloading of this tool is from within ‘ertdbdth()’ that is run within the if statement right before “HRDP Downloaded” is sent back to the C2.

STRRAT RE

This gives us another wshsoft.company IOC which is used to download the binary into the previously mentioned hrdpinst.exe binary, noting the mismatched extension here.

Network IOC: hxxp[://]wshsoft[.]company/multrdp[.]jpg

We’ve mentioned some of this previously; however we can now see in more depth what is occurring when “hrdp-new” is sent. Of interest is a new instance of an agafhas object being created, and if we investigate this further we see a message being sent to the C2 of “HRDP-SOC” before invoking ‘sdfsldf(fgfnbnc, Socket, dfhdfndfg)’.

STRRAT RE

Based on what we’re seeing we can begin to believe that this is acting as a socket to support HRDP, and looking at the invoked method we find it invokes another method ‘sdfsldf(Socket, dfhdfndfg)’ which finally kicks off another thread, this time of class ‘bmcvbmd(fgfnbnc, Socket, dfhdfndfg)’.

STRRAT RE

The above new thread creation is used on yet another class that implements the runnable class and invokes ‘sabretb(fgfnbnc, Socket, dfhdfndfg)’

STRRAT RE

By examining this further we find that this looks to be reading in an inputstream and determining whether it is sending the command “CLONE”, “EXITS”, or “EXIT”. In addition to this we can see if none of these are being sent it will create a socket tunnelling port 3389 (default RDP) to the C2 to allow RDP directly to the system.

STRRAT RE

Examining the clone thread we see evidence that this is being used to clone a user’s browser session via the new thread of ‘thrhhrth(fgfnbnc)’ based on an update being sent to the C2, before running an invocation of ‘dfhdfndfg.sdfsldf()’ which is defined within var2.

(new Thread(new thrhhrth(var0))).start();
var2.sdfsldf();

STRRAT RE

Taking a look at ‘fgfnbnc.sabretb(fgfnbnc)’ we can see that this is looking to determine if a file exists which is an object holding the cloned Firefox profile.

STRRAT RE

If this profile exists it will check to see if a Firefox.bat file has been dropped to the created user’s desktop, and if it has it will send a message that Firefox has been cloned. If it hasn’t, it will attempt to get a Firefox profile by running through ‘sbsbgrsg()’, and then from this attempt to clone the user’s Firefox profile by creating a launcher (a batch script, note the misspelling of launcher message also) which runs Firefox but uses the ‘–no-remote -profile’ parameters to specify starting with this found user’s profile.

STRRAT RE

STRRAT RE

If we instead look at the ‘EXITS’ flow, we see that this runs the below:

var0.sabretb();

Looking into this we find that it’s all about ‘cleaning up’ and covering tracks from running HRDP. In this instance it seeks to delete the temporary created account and restore visibility of the last logged on username at the logon screen.

STRRAT RE

Looking at the ‘EXIT’ flow, we see that this runs the below:

var0.gsbthstgb(var0.UserName);

The method takes in a String and then proceeds to load in Kernel32.dll so that it can check within sfsrgsbd() whether this is running on a 64bit OS or not. This is done so that the Wow64DisableWow64FsRedirection windows API function can be called to disable redicrections that occur on 32bit applications running on 64 bit OS’, before logging the user off and re-enabling this.

STRRAT RE

STRRAT RE

Based on this it’s inferred that ‘EXIT’ is used to logoff of the RDP session established. Finally if we examine the method ‘sdfsldf(String)’ within ‘fgfnbnc’, we find that this method is being embedded in checks performed whenever a new ‘net’ command is being run from this malware, with the same occurring in ‘sabretb(String)’.

STRRAT RE

What we can see here is that the malware seems to have some sort of checks in place for different “command completed successfully” messages which could be being resolved in the following languages.

  • English
  • Spanish
  • French
  • German
  • Italian

This may indicate that the intended targets of this malware or languages spoken of intended victims are that of the above. Regardless, from this we can infer that ‘hrdp-new’ is used to invoke a ‘Hidden’ RDP session which can clone user’s Firefox and Chrome browser sessions.

Command Functionality: hrdp-res

This command primarily functions from within the ‘fgfnbnc(String)’ class much like the previous command; however the primary difference is that this takes in a variable which acts as the defined username rather than randomly creating a new one after ‘null’ was previously passed in.

STRRAT RE

Not far into the routine a variable var2 is set to the username passed in, and checks are made using net commands to see if the user exists already or not and if it is in the local administrators group. Where it is then all checks are passed and the re-initialisation of HRDP won’t occur, but the session will be allowed to run.

From this we can infer that ‘hrdp-res’ is used to restore a ‘Hidden’ RDP session where a user was previously logged off using the ‘EXIT’ command.

Command Functionality: chrome-pass

This command primarily functions from within the ‘thtyrths()’ class.

STRRAT RE

After creating a new instance of this, it has the output of ‘sdfsldf()’ appended to the message sent to the C2. If we examine this method, it first checks if it is running Windows or not, and if it isn’t a message is displayed saying it is not supported, if it is it will proceed to run ‘sabretb()’.

STRRAT RE

If the output of this method is nothing then the message “No passwords Found” will be sent back, if it contains something, that will instead be returned, so let’s investigate ‘sabretb()’.

STRRAT RE

This leverages the JDBC driver SQLite Wrapper to interface with the Google Chrome password SQL database. From here it proceeds to retrieve all columns from the “logins” table and extract their relevant elements. From here STRRAT proceeds to use the DPAPI CryptUnprotectData function to decrypt the user’s stored Google Chrome passwords on this system.

In the event that using DPAPI fails to retrieve credentials, STRRAT falls back to running the below.

this.sdfsldf(var8);

STRRAT RE

STRRAT RE

Within this function we see a number of operations occurring, but probably one of the best assumptions as to why this occurs is made apparent from the below blog post.

Kirby Angell - Decrypting Browser Passwords & Other Secrets

As Kirby explains it, the the ‘Local State’ file which is being read in contains the DPAPI encrypted encryption key, and whereas older versions of Chromium (and hence Chrome) used solely the Login Data file and DPAPI, all passwords could be revealed using solely these and the relevant API (CryptUnprotectData), which we saw just before. Nowadays browsers need to first extract the internal key which has been encrypted from within the Local State file, and then calls CryptUnprotectData to decrypt this key. From here the key is then changed to a byte array and is used to create a secret key via AES encryption from this byte array, which is subsequently then used to decrypt the password given.

From this we can infer that ‘chrome-pass’ is used to decrypt usernames and passwords stored from within the logged on user’s Google Chrome profile.

Command Functionality: foxmail-pass

This command primarily functions from within the ‘dsgsdfge()’ class, specifically what kicks off from the ‘sdfsldf()’ function.

STRRAT RE

Looking into this further we can see that it is using a registry class ‘Foxmail.url.mailto’, presumably to get the install path of Foxmail. From this it is then looking for the Account.rec0 file stored within aan ‘Accounts’ subdirectory, and this is then being read to decode the Email and Password from within it.

STRRAT RE

The actual decoding occurs from within the class ‘sdfsldf(int, String)’, and we can tell this is encoding as it is performing permutations from fixed characters rather than any type of encryption using a schema such as AES.

STRRAT RE

The operations here occur a lot against an array of characters, and although we could go into each entry, it’s sometimes useful to explore OSINT to see if we can infer what is happening. Whilst researching these methods I stumbled across the following foxDecode program on Github which I believe may have been used to heavily influence how this command works. It has been cloned on the off chance it changes over time and can be found below:

JPMinty FoxDecode Fork

Comparing the first statement in this project we can see striking similarities in our decompiled code to that of the C Sharp program above, despite the differences in languages used.

STRRAT RE

Following on from this we see almost a like-for-like identical comparison occurring, with one making comparisons in decimal, whereas the other is checking the decimal hex equivalent value. For this to be a coincidence is highly unlikely, it’s almost certainly that this code has been ripped off and ported to function as part of this Trojan, and it’s not uncommon to have malware authors steal code as a means to an end.

STRRAT RE

STRRAT RE

The plus side to all of this is we have nicely annotated code and can understand what is happening… at this point I can’t not put this here.

STRRAT RE

Examining ‘sdfsldf(int, String)’ and how it applies to the rest of this code we find the following:

STRRAT RE

STRRAT RE

Based on this we can infer that ‘foxmail-pass’ is used to retrieve email addresses and decode passwords stored from within the installed Foxmail user profiles using ported C# code which is almost certainly plagiarised from ‘StarZHF’ and ‘Jacob Soo’.

Command Functionality: outlook-pass

This command primarily functions from within the ‘xncxbc()’ class, specifically what kicks off from the ‘sdfsldf()’ method.

STRRAT RE

Examining this method we can see that it looks to search through some specific registry keys for the current user which are associated with Outlook. In specifics this is searching for any entry which after being converted to lowercase simply contains the key “password”, if this is not found it will continue searching, but if it is found it will add this to an array called ‘object5’.

STRRAT RE

Further on from this we find that it will examine any key which contains “IMAP Password”, “POP3 Password”, “HTTP Password”, or “SMTP Password”, and will then pull out the corresponding SMTP Server, Email, and Password (after decrypting using ‘CryptUnprotectData’ which once again uses DPAPI).

STRRAT RE

Based on this we can infer that ‘outlook-pass’ is used to retrieve the SMTP Server, Email, and Password values stored from within the appropriate Outlook registry keys if they exist on the system.

Command Functionality: fox-pass

This command primarily functions from within the ‘dncbnf(boolean)’ class, this time things are a bit different when creating an instance of this object as it uses variables ‘sabretb’, and ‘sdfsldf’ in determining what is happening when this command is run, it also has an error message to indicate the command will be targeting Firefox.

STRRAT RE

Examining this class we find that upon being instantiated it will set a number of variables and run ‘this.bsgshsbs()’. This method looks to have support for both ‘Thunderbird’ and ‘Firefox’ based on examined strings; however, we need to take note that the object is being created with ‘false’ in this instance and as such will have a variable ‘dfhttegd’ set to ‘false’ that plays a key part in determining whether this is looking at Firefox or Thunderbird.

STRRAT RE

In the above we can see that this is specifically looking for a ‘profiles.ini’ file in the user’s roaming directory for Firefox to be used within a new object created from ‘nddfgndt(String)’, and is specifically looking for anything that starts with the string ‘Profile’ in the list of returned objects. The gist of this is that it will be extracting the relevant Firefox profile directory to then search for the files ‘logins.json’, ‘key4.db’, and ‘cert9.db’, prior to running the method ‘sdfsldf(File, File, File)’ over these 3 files.

STRRAT RE

The above clears up what this is doing. After locating the above files they will attempt to be compressed into an archive called ‘rpack.zip’ which is sent as a serialized sequence of data. This differs from the previously seen commands in that there are no attempts to decrypt or decode these files to retrieve the credentials in cleartext, but rather is exfiltrating 3 files ‘logins.json’, ‘key4.db’, ‘cert9.db’ in an attempt to clone the user’s Firefox profile. We can see these files are useful in doing so by looking at an answer given by ‘cor-el’ here on a Mozilla Support Forum, replicated below for convenience.

Firefox 58+ versions use key4.db for the key file (default salt and master password).
Previous Firefox versions used the key3.db file although they can use key4.db (SQLite).
Support for SQLite databases (key4.db and cert9.db) exists since 2011.
If you copy logins.json and key4.db to the current profile folder then Firefox should be able to find the usernames and passwords stored in logins.json.
Note the logins.json and key4.db should match.

Based on this we can infer that ‘fox-pass’ is used to retrieve the user’s ‘logins.json’, ‘key4.db’, and ‘cert9.db’ files from their respective Firefox profile in order to later decrypt these or import these into a Firefox session to steal credentials.

Command Functionality: tb-pass

This command primarily functions from within the ‘dncbnf(boolean)’ class much like the ‘fox-pass’ command, only this time it is being run with a ‘true’ value rather than a ‘false’ value being passed.

STRRAT RE

The sole difference between this command being run and the ‘fox-pass’ command being run is that this is instead looking for the common ‘Thunderbird’ working directories as opposed to the ‘Firefox’ ones. Examining this post on MozillaZine, a Mozilla documentation website created by the user community, we find this also contains ‘logins.json’, ‘key4.db’, and ‘cert9.db’ which can be used to recover stored passwords.

Based on this we can infer that ‘tb-pass’ is used to retrieve the user’s ‘logins.json’, ‘key4.db’, and ‘cert9.db’ files from their respective Thunderbird profile in order to later decrypt these or import these into a Thunderbird session to steal credentials.

Command Functionality: ie-pass

This command primarily functions from within the ‘dhgdghd()’ class.

STRRAT RE

Examining this class we find that this is actually one of the more simple classes. Upon being instantiated this object will launch a new powershell process and create a new object of type Windows.Security.Credentials.PasswordVault. From here it will run the RetrieveAll method to quite literally retrieve all the credentials stored in the Credential Locker. This will generally bring back hidden passwords; however, a quick pipe to RetrievePassword successfully extracts these credentials.

STRRAT RE

Once again if we weren’t sure about this we could do a search and wind up on Github with some code to retrieve credentials from IE & Edge which is identical.

# Content: Receive Credentials from IE & Edge
# Author: Florian Hansemann | @CyberWarship | https://hansesecure.de
# Date: 09/2020

[void][Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]
$vault = New-Object Windows.Security.Credentials.PasswordVault
$vault.RetrieveAll() | % { $_.RetrievePassword();$_ } | select username,resource,password

Based on this we can infer that ‘ie-pass’ is used to retrieve all the credentials stored in the Credential Locker used by Edge and Internet Explorer…and that malware authors rip code off from others, who’d have thought?

Command Functionality: all-pass

This command begins to chain a number of the above credential harvesting techniques specifically called out in ‘dhgdghd()’ (IE / Edge), ‘thtyrths()’ (Google Chrome), ‘dsgsdfge()’ (Foxmail), ‘xncxbc()’ (Outlook), before checking if the command passed was ‘save-all-pass’, in the event it was, it will send these to the C2 before further processing occurs.

STRRAT RE

From the above we also see that if the command didn’t include ‘save-all-pass’, STRRAT will continue processing ‘dncbnf(boolean)’ to get both Firefox and Thunderbird related files which will be sent to the C2.

Based on this we can infer that ‘all-pass’ is used to harvest credentials for IE/Edge, Google Chrome, Foxmail, and Outlook. In the event that ‘save-all-pass’ is being sent instead as the command, this will send all of these to the C2. If not, it will go out and try to retrieve Firefox and Thunderbird files needed to decrypt or import passwords.

Command Functionality: save-all-pass

This command is actually a part of the ‘all-pass’ command mentioned above and determines if credentials will be sent to the C2 or not.

Command Functionality: chk-priv

This command functions by simply running cbnfdhn.ssdgsbh() and sending back your privileges as either ‘Admin’ or ‘User’.

STRRAT RE

Just when we thought commands couldn’t be more simple we find this. By examining ssdgsbh() we can see that this is used to try and write a new file to ‘C:\Windows\System32\config\dummy.log’.

STRRAT RE

Because the location this file is being written to has a default Access Control List (ACL) which restricts it to Administrators, this command takes a shortcut method of trying to write a dummy file, and if it can’t do it just assumes you’re not an Administrator, cute.

Based on this we can infer that ‘chk-priv’ is used to try and write a dummy file to C:\Windows\System32\config\dummy.log as a way to check user privileges. Where it’s successful it will send back ‘Privilege: Admin’, and where it fails it will instead send back ‘Privilege: User’.

Command Functionality: req-priv

This command primarily functions from the ‘sstydgn()’ method inside of cbnfdhn which if the method returns ‘true’ will cause this process to terminate via System.exit(0).

STRRAT RE

The reason this is occurring can be uncovered easily as we dive into ‘sstydgn()’. Looking at this method we can see that it is attempting to launch PowerShell in order to use the ‘start-process’ commandlet to run STRRAT using ‘-verb runAS’. As this happens STRRAT will be attempting to use PowerShell to launch STRRAT as administrator which will generally function by sending a UAC prompt to the user asking if they want to run this.

STRRAT RE

If the user confirms this or UAC is not enabled then this will return true and kill the current STRRAT process as a new one will have been spawned with Administrator rights.

Based on this we can infer that ‘req-priv’ is used to use PowerShell in an attempt to run STRRAT with administrator rights/privileges.

Command Functionality: rw-encrypt

This command primarily functions from a newly created object of ‘sdfsldf(string)’.

STRRAT RE

What’s important here is that the method is passed in an string when it is created that gets assigned to ‘dfhttegd’, this will later be used as a passphrase.

STRRAT RE

There’s also a variable ‘sdfsldf’ which contains the string ‘.crimson’ and is used throughout this command being run. In summary this will take a passed string as a password, and upon being instantiated will locate the user’s Downloads, Documents, and Desktop directory and store this in an array. From here STRRAT will iterate over the array, and if a file exists at that location it will read in the bytes of that file into another array. From this it will use a passphrase stored in ‘dfhttegd’ and proceed to encrypt the bytes of the read in file one by one using AES-128 encryption. The encrypted bytes are then written back to disk at the same location, except with the ‘.crimson’ extension, and the original file is then deleted.

STRRAT RE

This highlights the importance of examining malware revisions for changes and citing your sources given a number of companies copy work by other researchers or companies without giving proper credit or performing the analysis themselves. At the time of writing I have identified at least 2 posts publicly which are still stating that this RAT doesn’t implement a ransomware routine and merely changes the extension of files on disk, and from what we can see here, this is simply not the case. The posts go into no technical detail and despite showing version 1.4 and 1.5 of STRRAT in the analysis, it’s mentioned this is a fake ransomware module. From at least version 1.4 this in fact does look to have a fully functioning ransomware routine as shown here, and I strongly believe anyone who thinks otherwise is citing (or not citing) old research without analysing the samples themselves.

Based on this we can infer that ‘rw-encrypt’ is used as a ransomware module to encrypt the user’s Downloads, Documents, and Desktop directory using AES-128.

Command Functionality: rw-decrypt

This command primarily functions from a newly created object of ‘sdfsldf(string)’ much like the ‘rw-encrypt’ command, with the difference being that the new thread being created is instead occurring via ‘dfghdmc’.

STRRAT RE

Examining this we can see that this will instead kickoff a method of ‘sabretb’ inside of ‘sdfsldf’.

STRRAT RE

Taking a look into this we find it is much the same as ‘rw-encrypt’, except with a difference being it is using the previously established object in ‘rw-encrypt’ and a passphrase down to decrypt the files of interest by reusing the ‘DecryptConfig’ method which we previously renamed. This method is more broadly used as a decryption method now, both for STRRAT’s configuration file, and also the ransomware decryption module.

STRRAT RE

Based on this we can infer that ‘rw-decrypt’ is used as the recovery method for STRRAT’s ransomware module and is used to decrypt the user’s Downloads, Documents, and Desktop directory using AES-128.

Command Functionality: show-msg

This command works as the message component of the executed ransomware and is very basic in how it works. First off it will get the location of the user’s desktop and create a file called ‘crimson_info.txt’ there. From here it will take a passed string to the command and write it within that file, and finally it will execute notepad to display the contents of this file to the user.

STRRAT RE

This is pretty unusual and bizarre to be honest, given most ransomware variants will encrypt a number of files and do this not only in multiple threads, but with multiple files being dropped and a message which is modular but baked into the ransomware sample. In comparison this seems to just give the functionality to either use this or not depending on what the operator wishes to do.

Based on this we can infer that ‘show-msg’ is used to create and display a ransomware (or arbitrary) message to a victim by creating a text file on the user’s desktop and using notepad to open it.

Command Functionality: screen-on

This command kicks off a new thread of object ‘fhjtjtg()’ which has been created.

STRRAT RE

Examining this we find it kicks off the method ‘bsgshsbs()’ from within ‘cbnfdhn’.

STRRAT RE

Examining this we can see that it is used to move the mouse ever so slightly as to keep the computer from falling asleep (prevent screensavers).

STRRAT RE

Based on this we can infer that ‘screen-on’ is used to move the mouse ever so slightly as to keep the screen on and prevent any computer screensaver.

Wrapping up flow and functionality:

At this point there’s not much we haven’t covered off in this particular piece of malware, we’ve well and truly reversed it and showed how we came to every conclusion along the way. The standard beaconing of STRRAT can be seen under the method ‘sbsgssdfg’ which we glossed over far earlier in this analysis piece. This sends certain information about the host that’s been infected back to the C2 in the form of a ‘ping’, which is actually a web request, and not an ICMP (ping) packet.

STRRAT RE

As the cherry on top we can see one last evidence of this retrieving the user’s publicly facing IP address by making a request to ip-api[.]com which is referenced within the method ‘sbsgssdfg()’ and ‘fgssdg()’.

STRRAT RE

Network IOC: ip-api[.]com/json/
Network IOC (User Agen): Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36

Part 4: Threat Intelligence and Detection Engineering

Detection Engineering

Yara Rule

To build out a Yara rule we’ll take a look at some of the most common IOCs seen in this malware as analysed above, and select a subset of these in addition to other commands seen embedded into the malware’s functionality:

Network IOCs:

hxxp[://]wshsoft[.]company/jre7[.]zip
hxxps[://]pastebin[.]com/raw/Jdnx8jdg
hxxps[://]pastebin[.]com/u/wshsoft
pluginserver[.]duckdns[.]org
hxxp[://]str-master[.]pw/strigoi/server/ping[.]php?lid=
moregrace[.]duckdns[.]org
hxxp[://]jbfrost[.]live/strigoi/server/?hwid=1&lid=m&ht=5
palaintermine[.]duckdns[.]org
ip-api[.]com/json/
hxxp[://]wshsoft[.]company/multrdp[.]jpg
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36

Host IOCs:

HKCU\Software\Microsoft\Windows\CurrentVersion\Run\ntfsmgr
64578lock.file
3219lock.file
Scheduled Task - "Skype"
HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System /v dontdisplaylastusername = 1

STRRAT/Dropper Specific IOCs:

carLambo
HBrowserNativeApis
config.txt
loorqhustq

Using the above we can create a fairly flexible Yara rule to detect this malware.

/*
   Author: @CyberRaiju
   Date: 2022-05-19
   Identifier: STRRAT-Identification
   Reference: https://www.jaiminton.com/reverse-engineering/strrat
*/

rule STRRAT_14 {
   meta:
	  description = "Detects components or the presence of STRRat used in eCrime operations"
	  license = "Detection Rule License 1.1 https://github.com/Neo23x0/signature-base/blob/master/LICENSE"
	  author = "@CyberRaiju"
	  reference = "https://www.jaiminton.com/reverse-engineering/strrat"
	  date = "2022-05-19"
	  hash1 = "ec48d708eb393d94b995eb7d0194bded701c456c666c7bb967ced016d9f1eff5"
	  hash2 = "0A6D2526077276F4D0141E9B4D94F373CC1AE9D6437A02887BE96A16E2D864CF"
   strings:
	  $ntwk1 = "wshsoft.company" fullword ascii
	  $ntwk2 = "str-master.pw" fullword ascii
	  $ntwk3 = "jbfrost.live" fullword ascii
	  $ntwk4 = "ip-api.com" fullword ascii
	  $ntwk5 = "strigoi" fullword ascii
	  $host1 = "ntfsmgr" fullword ascii
	  $host2 = "Skype" fullword ascii
	  $host3 = "lock.file" fullword ascii
	  $rat1 = "HBrowserNativeApis" fullword ascii
	  $rat2 = "carLambo" fullword ascii
	  $rat3 = "config" fullword ascii
	  $rat4 = "loorqhustq" fullword ascii
	  
   condition:
	  filesize < 2000KB and (2 of ($ntwk*) or all of ($host*) or 2 of ($rat*))
}

Performing a hunt using Hybrid Analysis reveals a number of hits on this malware, making this a useful Yara rule for finding samples. Of course this could be expanded out further, for example by adding checks for the dependencies STRRAT is known to use, but when creating these it’s always a balancing act of being broad enough to net new samples, but not get riddled with False Positives, but not too confined as to miss different variants.

Snort Rule

To build out a Snort rule we’ll take a look at some of the network traffic described above, specifically zoning in on the beacon STRRAT sends frequently, and look for any tcp traffic to or from a server that contains the content “ping”, a pipe, and then “STRRAT” (this is as simple as converting it to hexadecimal and looking for the content, and giving it a unique sid). This could be expanded further to detect commands being sent to/from the C2; however, this will be a task left for the reader.

alert tcp any any -> any any (msg:"STRRAT C2 Beacon Detected"; content:"|70 69 6e 67 7c 53 54 52 52 41 54|"; priority:2; reference:url, www.jaiminton.com/reverse-engineering/strrat;sid:1120009; rev:1;)

In this example we’re going to use a pcap already captured previously from a system infected with a STRRAT variant provided by Brad Duncan to test the rule.

After setting up a few preprocessors for stream5_global, we’re good to test the rule over our pcap. By creating a snort config with the below (stored in a file called strrat.rules).

preprocessor stream5_global: track_tcp yes, \
   track_udp yes, \
   track_icmp no, 

alert tcp any any -> any any (msg:"STRRAT C2 Beacon Detected"; content:"|70 69 6e 67 7c 53 54 52 52 41 54|"; priority:2; reference:url, www.jaiminton.com/reverse-engineering/strrat;sid:1120009; rev:1;)

We can easily test this using snort on a linux VM. Specifying the pcap file and testing against our created rule..

sudo snort -A fast --pcap-single=./pcap.pcap -c ./strrat.rules -l /var/log/snort

We can see this has 73 hits over the pcap, and has generated a number of alerts due to the consistent beaconing this malware presents.

STRRAT RE

Sigma Rule

Because STRRAT leverages a lot of native Windows utilities to enable persistence and execute commands, we can develop a fairly basic Sigma rule to detect possible infections of STRRAT. This could be expanded further to pick up on individual registry key creations from a sysmon event, or look at the behaviour of file writes; however, this will be a task left for the reader. This also hasn’t been confirmed for false positives which may need to be excluded.

title: STTRAT Child Process Spawning
status: experimental
description: Detects suspicious child processes of Java(w) possibly associated with a STRRAT infection.
author: Jai Minton (@CyberRaiju)
references: https://www.jaiminton.com/reverse-engineering/strrat
date: 2022/05/28
modified: 2022/05/28
tags:
    - attack.initial_access
    - attack.persistence
logsource:
    category: process_creation
    product: windows
level: high
detection:
    selection:
        - ParentImage|endswith:
            - '\java.exe'
            - '\javaw.exe'
    selection_2:
        - Image|endswith:
            - '\hrdpinst.exe'
            - '\cmd.exe'
    selection_cmd:
        CommandLine|contains:
          - 'schtasks /create /sc minute /mo 30 /tn Skype'
          - 'reg add'
          - 'shutdown /r /t 0'
          - 'shutdown /s /t 0'
          - 'wmic /node:. /namespace:'
          - 'hrdpinst.exe'
    condition: selection and selection_2 and selection_cmd
falsepositives:
    - Legitimate calls of various java applications to system binaries with specific command lines
level: high

Useful Windows Event Logs

The following event log identifiers will be useful to track this piece of malware:

Microsoft-Windows-TaskScheduler/Operational

  • 201 (Task registered)
  • 129 (Task Launched)

Security

  • 4688 (Process Creation)
  • 4698 (Scheduled Task Creation)
  • 4700 (Scheduled Task Enabled)

Sysmon

  • 1 (Process Creation)
  • 11 (FileCreate)
  • 12 (Registry Create and Delete)
  • 13 (Registry Value Set)
  • 22 (DNS Query)

Threat Intelligence - The Crimson Shadow

Note: Threat intelligence is a broad term here which has been used to cover getting extended telemetry on the threat this particular malware poses to organisations and people around the world, and potential victims.

During analysis I was presented with an interesting opportunity to track this malware on a global scale due to the way it functions. In late 2021, over the course of a month, infections of STRRAT around the world were tracked and plotted on a map based on the infected client’s IP address and Maxmind’s GeoLite2 Free Database. This clustered data is shown below.

STRRAT RE

By overlaying a heat map here we can see that although the numbers aren’t massive, the number of locations globally which had seen this malware execute were quite diverse.

STRRAT RE

Further, each cluser point represents a single IP, and doesn’t take into consideration the number of hits from that particular IP. To allow this to be easily navigated I overlayed IP address information along with ASN and count.

STRRAT RE

The method to turn the gathered data into such a map was to take a carefully crafted spreadsheet containing the data and plot it with Python3, pandas, and folium. The script created is show below (special thanks to umar-yusuf’s blog which helped get me started on this venture).

import pandas as pd

from IPython.display import display
import folium
from folium import plugins
from folium.plugins import HeatMap
import branca.colormap
from collections import defaultdict
import sys
import time

steps=5
colormap = branca.colormap.linear.YlOrRd_09.scale(0, 2).to_step(steps)
gradient_map=defaultdict(dict)
			 
for i in range(steps):
	gradient_map[1/steps*i] = colormap.rgb_hex_str(1/steps*i)

lon, lat = 10.626065, 43.035950

for ii in range(1,33):

 m = folium.Map([lat, lon],tiles='cartodbdark_matter', zoom_start=2)
 sheet='Day'+str(ii);
 heatmap_df = pd.read_excel(open('MapDataDaily.xls','rb'),sheet_name=sheet)
 count=sheet+" ("+str(len(heatmap_df))+")"
 title_html = '''
			  <h3 align="center" style="font-size:16px"><b>{}</b></h3>
			  '''.format(count)   

# heatmap_df.head()

 for i in range(0,len(heatmap_df)):

  folium.CircleMarker(
   location=[heatmap_df.iloc[i]["Latitude"],heatmap_df.iloc[i]["Longitude"]],
   radius=heatmap_df.iloc[i]["Value"]*0.001,
   line_color='#fc0000',fill_color='#fc0000',color='#fc0000').add_to(m)

 m.get_root().html.add_child(folium.Element(title_html))
 m.save(sheet+".html")

After running over the data I had an interactive HTML page for each and every day of data collection and quantity of infected, unique IPs on any given day. This was then turned into the below gif. Note: Day 5’s data got lost during collection (call it user error).

STRRAT RE

In addition the most common hour period that hosts were seen to be infected (UTC) was also plotted over time.

STRRAT RE

Statistics

From the data gathered during this one month period, the following became apparent:

  • Approximately 5726 Unique IP addresses were seen to have been infected during this time.
  • 6 out of 7 continents had seen to have been infected during this time (Antarctica was the odd one out).
    • Africa: 589 IPs
    • Asia: 2446 IPs
    • Europe: 1454 IPs
    • North America: 892 IPs
    • Oceania: 125 IPs
    • South America: 220 IPs
  • Systems in approximately 101 countries had seen to have been infected during this time.
  • Systems in approximately 1005 cities had seen to have been infected during this time.
  • Approximately 100 different license keys appeared to be in use by STRRAT globally during this time.

Limitations and acknowledgements:

  • Given telemetry was captured from a network level, there could have been multiple systems resolving to the same IP due to NAT which remains an unknown in the above figures.
  • Some of the above figures were likely sandboxes or testing devices and didn’t indicate a compromised organisation, a large subset was still found to have frequent enough telemetry and during likely business hours to assume a compromised system.
  • Where a system was likely to be infected as identified above, this was mostly attempted to be reported to the relevant country Cyber Security / CERT Authority. Only a number of those contacted replied, and of these fewer confirmed an investigation of a machine occurring (some is better than none right?).

OSINT

By leveraging information known about this malware and the assumption it is likely being sold online or shared amongst groups, I was able to use OSINT to find the website being used to sell this piece of malware. From this we have a redacted screenshot which has the seller trying to give off the impression that this is “legitimate software” which you need to pay for on a monthly or 3 monthly basis. Despite this it says it is for “educational use only” which is a common theme amongst those developing tools they almost certainly know will be used for malicious purposes, it has fake location information, and it’s marketed with skulls and the grim reaper, and it accepts payments in only Perfect Money or Bitcoin.

STRRAT RE

An interesting component of the above is that the author is selling this capability on a monthly ($80) or 3 monthly ($200) basis, in addition it is touting the exact features of the malware which we’ve uncovered above and shown exactly how they work.

Part 5: Miscellaneous Trivia

Striogoi References

Throughout this analysis we see numerous references to ‘Strigoi’. From a search of Wikipedia and Strigoi fandom wiki pages, it was found that ‘Strigoi’ is a term from Romanian mythology which is used to refer to an evil, or troubled spirit, which can possess (or transform) into an animal. This has become synonymous with vampirism where a bat is said to have been posessed by a Strigoi, or are instead a Strigoi taking the form of a bat.

Concept art for this creature which I find appropriate is below, created by DavyWagnarok.

Strigoi artwork by DavyWagnarok:

Strigoi artwork by DavyWagnarok

Based on this we may infer that the creators are of Romanian descent or are targeting Romanian customers/users; however, this would be too easy and it could very much be that this is used as a method of misdirection. Nonetheless it does lead us to believe that the malware author felt ‘Strigoi’ to be a ‘cool’ enough term to be used for the name of this malware, whether or not they’re happy with a shortened ‘STRRAT’ being used globally based on the strings found in this malware is unknown.