Reviewing Qakbot loader sequences: Part 3

Posted:
10/18/2023
| By:
Blake Eakin

In the previous parts of this series, we reviewed the first steps in delivering Qakbot by examining log-based indicators around users opening the various possible phishing payloads that were historically used. Now, we’ll turn our attention to the other end of the Qakbot delivery process and focus on indicators around the execution of dynamic-link libraries (DLLs).

DLL execution paths

All execution paths leading to Qakbot pass through the execution of a DLL that injects the final loader payload into a system process, making it a crucial bottleneck for detection. If no other detection was triggered on any of the preceding activities, then we at least knew that the activities would end up here, which helped it become a big focus for detection opportunities.

While active, we found a lot of success detecting Qakbot incidents based on the unique names of the entry points they would use as posted by @pr0xylife or @Cryptolaemus1. Here, we will explore various detection strategies that present themselves when considering all their DLL execution methods that can help detect similar sorts of suspicious DLL execution without relying on frequently changing indicators, such as the specific entry point names.

Please note any of the detections we outline use regular expressions to help pinpoint targeted positions in the command line of process creation events. In these cases, we are using the Elasticsearch syntax.

Rundll32, Regsvr32, and DLLs

Much has already been said about Rundll32 and Regsvr32 and how they can be used for malicious purposes. So, we’ll provide only a quick summary.

DLL files store binary code that is meant to be called by other software so that it can be referenced by multiple code bases without having to be embedded in each piece of software that uses it. They have what are referred to as “entry points,” which are the actual function calls that the DLL makes available for use. Malicious actors make use of DLLs by writing them with malicious code in the places where execution is expected. For example, the DllMain entry point or other entry points that are expected to be called depending on the planned execution method.

Rundll32 is a very simple system utility that’s been available on every Windows version since Windows 95. It can be used to call DLLs directly without writing code to evoke them. You pass it the library path, the name of the entry point you want to call, and any parameters you want to pass to it, if necessary, and Rundll32 will run it for you.

Regsvr32 is a utility for registering and unregistering DLLs in the registry for various reasons, such as DCOM object usage. It depends on the DLL having either a DllRegisterServer or DllUnregisterServer entry point and is often seen during software installation.

Both of these system tools are frequently used by attackers in general as a way to proxy the execution of their malicious code by using a trusted, commonly used binary many times to inject their final payload into another process’s memory. Here, we present to you a variety of signals that attempt to cut through the noise of the normal usage of these tools to find generally suspicious behaviors that may indicate malicious execution.

A note on tuning and false positives

All the detections explored in this document are referred to as “weak signals.” This is not because the detections themselves are weak but because they are more general with the intention of detecting previously unknown threats and behaviors. This is in contrast to “strong signals,” which directly indicate specific known threats and behaviors. The downside to weak signals is that they tend to generate more false positives. What makes each of the following detections useful is that they are generally rare behaviors that shouldn’t generate too much noise, but they all come with the caveat that they may require tuning and adjustments specific to your environment.

Suspicious parent processes

DLL execution for a process injection may be the end goal of the Qakbot loader process, but it is still just a step in the execution chain, so it’s important to consider what comes before it. Most often, the DLL execution step would be executed by a script-based parent process. This is primarily rare to observe and lends itself to simple detections. The parent processes launching Rundll32 that we frequently saw with Qakbot were things like wscript.exe, powershell.exe, and mshta.exe.

Detections for these as parent processes to rundll32.exe are effective with generally low noise. A parent process of cmd.exe can be a bit noisier, but tunings should be manageable for expected activity. Additional detections around other native utilities that may allow local script or code execution, such as cscript.exe, hh.exe, or msbuild.exe, could also provide effective detections for suspicious child processes like Rundll32.

Non-typical library file extension

One of the most frequently observed behaviors—about 67% of our recorded executions—was the use of file extensions for library files called by either Rundll32 or Regsvr32 that weren’t DLL extensions. In many cases, other known library file extensions were used, but it may be best to start with our base detection and then add other extensions common for use cases specific to your environment.

To specifically target library file extensions in a command line execution of either Rundll32 or Regsvr32, we can look for process creation events for either rundll32.exe or regsvr32.exe. Then, we’ll include a regular expression search on the command line that includes the call for the process followed by what’s likely a file extension somewhere else following it on the command line. This looks like:

/.*([Rr][Ee][Gg][Ss][Vv][Rr]|[rR][uU][nN][dD][lL][lL])32(\.[eE][xX][eE]|[^.]).*\.[A-Za-z0-9]+.*/

We include the call to the process executable in the regular expression because it may or may not have the .exe extension on the end, which, without referring to it directly, may have that confused as the library extension. Otherwise, we are pretty loose with the regular expression since we’re using it on a command line for something very specific.

Finally, the detection also has an exclusion for what would be expected library extensions in the form of another regular expression along the lines of /.*\.[dD][lL][lL].*/. Again, we are very loose with this since we’ve already done a lot to make what we’re matching against very targeted, so it suffices just to know that we’re looking at something that matches all the previous filters but doesn’t include .dll in some form on the command line.

Other valid file extensions for library files in your environment may include OLE Control Extension files (.ocx), .tmp extensions seen frequently during installations, .cpl files, and potentially more. We would suggest trying to exclude these by including additional filters for the specific parent processes or other indicators that may remove those specific false positives while still alerting on non-typical library file extensions, but your mileage may vary. You may also want to consider breaking the regsvr32 and rundll32 detections into two and tuning specifically for each one.

Suspicious library paths

Threat actors tend to use several different file paths over and over, oftentimes due to the certainty of availability and permissions. They are a double-edged sword for attackers, though. While a large number of different activities would be expected around these directory paths, there are also numerous situations that are not as typical. In many cases, this gives detection engineers an easy way to detect atypical behaviors.

Qakbot made heavy use of many of these common suspicious file paths, and in a large enough variety that studying the paths Qakbot alone made use of would give a great foundation for detecting the behaviors of a wide variety of other malware. Here, we explore detecting these suspicious paths as they were exposed in process creation events for Qakbot’s DLL execution.

Suspicious Base Paths Used by Qakbot
C:\ProgramData\
C:\Users\[username]\Documents\
C:\Users\Public\
C:\Users\[username]\AppData\
C:\[non-typical root folder name]\

First, we can detect for direct evocation of a full path to the library being called by either Rundll32 or Regsvr32. This is a simpler task in most cases for Regsvr32 since it has a much more predictable execution syntax where we can mostly expect there only to be one path exposed on the command line—in addition to a potential complete path to regsvr32.exe, but that doesn’t hinder our abilities here.

In these cases, a simple detection for regsvr32.exe process creation with most of these paths in the command line will suffice. The only exceptions to this assumption for Regsvr32 could potentially arise when using the /I switch for including a command-line string, which might possibly include another path in the command line. As far as we’ve observed, this usage is rare.

This is more difficult for Rundll32 executions since its regular usage will frequently include parameters for entry points, called using the utility, so the possible variations in how Rundll32 is called has a lot of room to include other paths as parameters. However, we do know that the path we are interested in, the path to the library, will have a predictable position relative to other components of the command line call. We can use this to construct a regular expression that targets these suspicious base paths by searching for any sort of command line string followed by one of the paths:

/((\"[^\"]+\")|([^ ]+)) +\"?[cC]:\\(([pP][rR][oO][gG][rR][aA][mM][dD][aA][tT][aA])|([uU][sS][eE][rR][sS]\\(([pP][uU][bB][lL][iI][cC])|([^\\]*\\(([dD][oO][cC][uU][mM][eE][nN][tT][sS])|([aA][pP][pP][dD][aA][tT][aA]))))))\\.*/

Another notable pattern we see in Qakbot DLL executions that is rarer to see in normal usage is the use of relative paths to call libraries. Most typical DLL execution with either of these two utilities will either refer to a full path to the library or will refer to a library directly that exists in the PATH variable.

The regular expression approach for this can be a little bit different between the two system binaries we are reviewing. For Rundll32, we’re just looking for any library path that directly starts with the name of a directory instead of a drive letter or with “.\”,; though this notably doesn’t catch when libraries are evoked that are in the current working directory:

/((\"[^\"]+\")|([^ ]+)) +\"?(([a-zA-Z0-9]|(\.\\))[^:\\, ]+\\)+.*/.

The Regsvr32 version of the relative path regular expression is similar but takes into account some of the utility’s possible command line options:

/((\"[^\"]+\")|([^ ]+)) +( *[-\/][uUsSnN] *)*\"?(([a-zA-Z0-9]|(\.\\))[^:\\]+\\)+.*/.

Regsvr32 entry points called via Rundll32

As we explained earlier, Regsvr32 requires that libraries passed to it have either a DllRegisterServer or DllUnregisterServer entry point that is supposed to take the actions necessary to install the library in the registry. During 2022, while mostly pushing Office maldocs, Qakbot mainly used Regsvr32 for DLL execution purposes, only on rare occasions calling Rundll32 instead. When it did, it maintained usage of these two entry points.

This is a fairly rare activity to observe and makes for a decent detection of atypical behaviors. We don’t have to get too fancy with it either. Process creation for rundll32.exe processes with either the DllRegisterServer or DllUnregisterServer entry points will be effective enough with a few possible false positives that should be easy to tune out.

Rundll32 execution with no entry point

Qakbot kept defenders on their toes, sometimes daily, with the constantly changing entry point names they used. Sometimes, they would follow a predictable pattern, and sometimes, they would just be random words. Other times, they would use the same entry point name expected for another library. But near the end of their most recent run, they just stopped using entry point names completely. This turned out to be an extremely rare expression of possible rundll32 usage. This prompted the creation of a detection that looks for process creation events where either the process or original file name is rundll32.exe, with the following regular expression that searches the command line:

/((\"[^\"]+\")|([^ ]+)) +((\"[^\"]+\")|([^- \/,]+))/

This locates two separate command line strings—either strings bounded by double quotes or without spaces in them, not including any commas. If you have the ability to count command line arguments in your detections, then you may also be able to look for rundll32 executions where there are two arguments and no commas.

Numbered entry points

When calling entry points with Rundll32, it’s not required to call them by name. A numeric identifier can also reference all entry points, and many threat actors have been known to use these to obscure their activities when calling entry points for system libraries. For example, the MiniDump entry point of comsvcs.dll is a well-known method for dumping process memory, which can be used for extracting passwords from processes such as lsass. Rather than directly invoking the MiniDump name on the command line and drawing suspicions, it has also become common to use its numeric identifier of 24, which has stayed consistent across Windows versions.

Qakbot occasionally called their DLLs with numeric entry points, though not as frequently as a lot of other methods. However, calling by numeric entry point is rare enough that it makes an effective detection that may require some easy tuning based on regularly used applications. This can be detected by looking for process creation events for rundll32.exe with a regex on the command line, such as the following:

/((\"[^\"]+\")|([^ ]+)) +((\"[^\"]+\")|([^ ]+)),? *\#[0-9+].*/

This searches for a command followed by another command line entity, such as a quoted path or string of characters with no space, that’s directly followed by a # and a number or + sign—which has been used to obfuscate numeric entry points.  

Potential DLL execution alternatives

Rundll32 and Regsvr32 were what Qakbot opted to use for DLL execution, but there are many more methods attackers can leverage to execute libraries. If anything, Qakbot has taught us that we can’t just rest on known behaviors for detections.

To that end, we need to consider viable alternatives that either the same actors or other groups may use. There is no shortage of dependably available substitutions. The Control Panel can be leveraged, considering that CPL files are actually DLL files. Its system binary control.exe will call a DLL’s CPlApplet entry point. Msiexec can be used as a stand-in for Regsvr32, calling a library’s DLLRegisterServer or DLLUnregisterServer entry points when called with “/y” or “/z” switches, respectively. System binaries like Mavinject can stand in for the whole injection process just by injecting the DLL itself into a process. Even Netsh can be used for arbitrary DLL execution by adding it as a helper and then calling netsh.exe.

Conclusion

It’s apparent that even just considering this point in an execution chain can expose a large surface for attackers to shift up their procedures to create detection difficulties. But by reviewing a broader picture of past collective procedures, certain patterns can start to emerge that clue us into rare and suspicious behaviors that can form weak signals useful for catching malicious activity before needing to know about it via external intelligence.

Now that we have reviewed the first and last steps of the Qakbot loading process, the next and final installment of this Qakbot series will cover the other steps in between that present detection opportunities.

Special thanks to Austin Shelton, security analyst and fellow ConnectWise colleague, for his contributions in putting this blog together.