Rundll32: The Infamous Proxy for Executing Malicious Code

Today, we are going to dwell on a Microsoft tool, the infamous rundll32.exe, which allows you to load and execute code. It is often used by adversaries during their offensive operations to execute malicious code through a process which we will explain in detail.

What is Rundll32.exe?

Rundll32.exe is a Microsoft-signed binary used to load dynamic link libraries (DLLs) in Windows. It is native to Windows and present in both 32 and 64 bit versions, respectively present in these places:

C:\Windows\System32\rundll32.exe
C:\Windows\SysWOW64\rundll32.exe

 

Here are the signing details:

rundll32 signature

Figure 1 - Rundll32.exe signature overview

 

rundll32 signature details

Figure 2 - Rundll32.exe signature details

 

On the one hand, rundll32.exe is an executable signed by Microsoft which is natively present on all Windows systems; on the other hand, it is also very flexible and efficient for loading code into memory, acting as a proxy for this purpose. Moreover, because rundll32.exe benefits from a certain degree of trust, it could be a possible AppLocker and Software Restriction Policies (SRP) bypass.

Last but not least, rundll32.exe is also able to help to dump the memory of processes, such as the LSASS (Local Security Authority Subsystem Service) process to retrieve credentials, which we will demonstrate.

For these reasons, it is a very interesting and frequently used tool by adversaries to proxy execution of arbitrary malicious code and dump LSASS memory. This technique is also mapped and described in the MITRE ATT&CK™ Enterprise framework, rundll32.exe has its own sub-category, as below:

Tactic

Defense Evasion

Technique ID

T1218 .011

Technique Name

System Binary Proxy Execution

Sub-technique Name

System Binary Proxy Execution: Rundll32

Defense Bypassed

Anti-virus, Application control, Digital Certificate Validation

 

Who is Currently Abusing Rundll32.exe?

Although rundll32.exe has frequent and undeniable legitimate use, it is also taken advantage of by many attackers, ranging from state-affiliated groups (APTs) to cybercriminal groups to proxy execution of malicious code. 

Some of these threat actors are among the most sophisticated attackers around and still rely on rundll32.exe during their operation. Just to cite a few, we can have:

We could also note that tools such as Cobalt Strike can use rundll32.exe to load DLL from the command line. This list could be much longer but the idea is to briefly summarize the importance, dangerousness and diversity of these groups that rely on rundll32.exe, so it is important to understand its mechanism to detect it.

How Does Rundll32.exe Work?

Microsoft does not provide detailed information about rundll32.exe, but they mention the syntax below:

Microsoft documentation for rundll32

Figure 3 - Microsoft documentation for rundll32.exe

 

The syntax is not perfectly correct, you have to specify an entry point into the dynamic-link library (DLL) as below (otherwise nothing will happen):

rundll32.exe <DLL PATH>,<DLL Entry Point>

 

Note that <DLL PATH> could be both local and remote when the DLL is hosted on an accessible share, using UNC (Universal naming convention) paths in the second scenario.

Even if the entry point does not exist, the system will calls the DllMain function with the DLL_PROCESS_ATTACH value first and the relevant code will be executed followed by an error message because of the missing entry point.

Indeed, rundll32.exe process uses the LoadLibraryExW function which calls DllMain (DLL’s entry point) and load the DLL into the virtual address space of the rundll32.exe process as we notice below:

image17-Aug-04-2022-09-20-06-04-PM

Figure 4 - Rundll32.exe relies on LoadLibraryExW function

 

Analyzing the stack, we notice a LoadLibraryExW call with:

  • ESI register containing the DLL Path (C:\\Code\\RUNDLL32_TEST.dll)
  • EDI register containing the entry point (BADENTRY)
  • We also pushed values 8 and 0

To better understand these values, let’s have a look at LoadLibraryExW function’s (from libloaderapi.h) syntax:

HMODULE LoadLibraryExW(       // From this example:
  [in] LPCWSTR lpLibFileName, // L”C:\\Code\\RUNDLL32_TEST.dll”
       HANDLE  hFile,         // 0
  [in] DWORD   dwFlags        // 8
);

 

The following parameters are used:

  • lpLibFileName: This string specifies the file name of the module to load
  • hFile: This parameter is set to NULL (it is reserved for future use)
  • dwFlags: The search strategy is set to 8 which correspond to LOAD_WITH_ALTERED_SEARCH_PATH

Know that LoadLibraryExW is theoretically able to load a DLL module without calling the DllMain function of the DLL but here, rundll32.exe is using dwFlags set to 8 which isn’t inducing this behavior. The code is equivalent to:

hLibModule = LoadLibraryExW(<DLL PATH>,(HANDLE)0x0,8);
  if (hLibModule == (HMODULE)0x0) {
    res = GetLastError();
    if (res == 0xc1) {
     // ...
    }
    else {
      Arguments = (LPCWSTR *)0x0;
      dwFlags = 0x1200;
     // ...
    }
// ...

 

Note: As per Microsoft API documentation, when rundll32.exe calls the DllMain function with any specific entry point, (i.e. a value other than DLL_PROCESS_ATTACH), the return value is ignored. If the return value is FALSE when DllMain is called during process initialization, the process terminates with an error and GetLastError is called to provide extended error information.

Could We Create a DLL to Test It?

We will now create a simple 32-bits C++ DLL named RUNDLL32_TEST.dll which will execute calc.exe once the DllMain function is called with the DLL_PROCESS_ATTACH value and we will create a specific exported entry point to execute cmd.exe. Let’s also provide debug information using MessageBox:

#include "pch.h"
#include <windows.h>

// Reference:
// https://docs.microsoft.com/en-us/windows/win32/dlls/dllmain
// https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-entry-point-function

BOOL APIENTRY DllMain(
    HMODULE hModule, // handle to DLL module (same as HINSTANCE)
    DWORD fdwReason, // reason for calling function
    LPVOID lpReserved // reserved
) {

    STARTUPINFOA si = {
      sizeof(STARTUPINFOA)
    };
    PROCESS_INFORMATION pi;
    LPCSTR appCalc = "C:\\Windows\\System32\\calc.exe";

    // Perform actions based on the reason for calling
    switch (fdwReason) {

    case DLL_PROCESS_ATTACH:

        // A process loads the DLL (initialize once for each new process)
        // Return FALSE to fail DLL load

        // Start a "calc.exe" child process 
        if (!CreateProcessA(appCalc, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
            MessageBox(NULL, TEXT("CreateProcessA() failed\n") + GetLastError(), TEXT("Error"), MB_OK | MB_ICONINFORMATION);
            return FALSE;
        }

        MessageBox(NULL, TEXT("Hello, DLL is attached"), TEXT("Hi!"), MB_OK | MB_ICONINFORMATION);
        return TRUE;

    case DLL_THREAD_ATTACH:
        // Do thread-specific initialization
        break;

    case DLL_THREAD_DETACH:
        // Thread exits normally
        // Do thread-specific cleanup
        break;

    case DLL_PROCESS_DETACH:
        // A process unloads the DLL
        // Perform any necessary cleanup
        break;

    }
    return TRUE; // Successful DLL_PROCESS_ATTACH
}

// Export function
extern "C"
__declspec(dllexport) void SpecificEntryPoint() {

    MessageBox(NULL, TEXT("Hello from a DLL exported function"), TEXT("Hi!"), MB_OK | MB_ICONINFORMATION);

    STARTUPINFOA si = {
      sizeof(STARTUPINFOA)
    };
    PROCESS_INFORMATION pi;
    LPCSTR appCmd = "C:\\Windows\\System32\\cmd.exe";

    // Start a "cmd.exe" child process 
    if (!CreateProcessA(appCmd, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
        MessageBox(NULL, TEXT("CreateProcessA() failed\n") + GetLastError(), TEXT("Error"), MB_OK | MB_ICONINFORMATION);
    }

}

 

It is important to note that even when targeting a specific entry point (DLL-exported function), the code within the DllMain function from DLL_PROCESS_ATTACH will still be executed. 

Note: This behavior was previously discussed, it is related to the dwFlags set to LOAD_WITH_ALTERED_SEARCH_PATH when calling LoadLibraryExW from rundll32.exe (this flag is not under control).

With our DLL, we will have two applications executed (calc.exe and cmd.exe) and two message boxes in this scenario when using the SpecificEntryPoint.

Finally, please note that it is not a good practice to call CreateProcess within DllMain, as it could lead to improper synchronization and cause an application to deadlock as creating a process can load another DLL.

Let’s Play with Our DLL!

Let’s first execute calc.exe (DllMain function with the DLL_PROCESS_ATTACH value and a fictive but mandatory entry point):

rundll32.exe RUNDLL32_TEST.dll,ThisEntryDoesNotExists

 

This command, targeting a fictive entry point (non-existing DLL-exported function), will result in the execution of calc.exe because of the previously mentioned point.

Note: Without an entry point, even non-existing one, the DLL will not be loaded (despite what Microsoft’s documentation describes).

Now, let’s play with our DLL-exported function named SpecificEntryPoint to execute cmd.exe using the following command:

rundll32.exe RUNDLL32_TEST.dll,SpecificEntryPoint

 

We previously made an observation about LoadLibraryExW which was important as the specified module is loaded into the address space of the calling process (rundll32.exe), hence we could recover loaded modules inspecting the thread’s stack as we can see below:

image5-Aug-04-2022-09-21-44-63-PM

Figure 5 - Thread’s stack overview (reaching a provided DLL’s entry-point)

 

As result, we could notice a spawned cmd.exe with non-existing parent because the rundll32.exe process (PID 1844) is terminated and cmd.exe process (PID 10904) was created as a new and independent process:

image10-Aug-04-2022-09-22-17-74-PM

Figure 6 - Process overview (rundll32 spawned a child with “unmapped” parent)

 

However, thanks to the Cybereason Defense Platform, we could examine the history, all loaded modules and all other relevant information and also visualize the processes tree to notice that rundll32.exe is the parent of cmd.exe:

image21-Aug-04-2022-09-23-12-61-PM

Figure 7 - Cybereason’s investigation dashboard to hunt for rundll32.exe executions

 

image13-Aug-04-2022-09-23-56-58-PM

Figure 8 - Visualization of the rundll32.exe Process Tree using Cybereason

 

This example isn’t malicious by itself therefore we wouldn't raise a MalOp™ for this execution, nevertheless, all relevant context and information are stored and accessible.

What Else Could Rundll32.exe Do?

We have seen that rundll32.exe is a powerful asset for adversaries to proxy execution of arbitrary and malicious code. This binary has another ace in the hole, it could leverage comsvcs.dll (a Microsoft-signed DLL) which exports a function called MiniDumpW that rely on MiniDumpWriteDump to dump lsass.exe (Local Security Authority Subsystem Service) process memory to retrieve credentials.

MITRE ATT&CK Mapping: This technique is named "OS Credential Dumping: LSASS Memory" and has the ID T1003.001. 

The syntax is as below:

rundll32.exe C:\windows\System32\comsvcs.dll, MiniDump <LSASS PID> <DUMP PATH> full

 

Where <LSASS PID> is the process ID of LSASS and <DUMP PATH> the output to be written. Note that this requires Local Administrator or SYSTEM privileges.

Example:

image2-Aug-04-2022-09-24-44-31-PM

Figure 9 - Rundll32.exe using comsvcs.dll to dump LSASS process memory

 

We demonstrated that rundll32.exe, which is a signed Microsoft binary, helps to proxy execution of arbitrary and malicious code and dump credentials. 

Anything else, please? Of course! This binary is also able to:

  • Execute JavaScript
  • Execute VBScript
  • Execute content from Alternate Data Stream (ADS)
  • Manipulate COM Objects
  • Etc.

You can refer to the LOLBas projects for further information and examples.

How Does Cybereason Detect Rundll32.exe?

Thanks to our advanced AI-powered technology, we offer the perfect alternative to manually triaging and investigating a flood of alerts full of false positives: the Cybereason MalOpan interactive graphical display of the full malicious operation from root cause. Instantly see all elements of an attack correlated across every impacted asset with the option for automated or one-click remediation, reducing investigation periods by as much as 93% so Defenders can eliminate threats in a matter of minutes rather than days.

Indeed, on the one hand, we already noticed that Cybereason is able to avoid false positives about benign use of rundll32.exe, using our test DLL to spawn another Windows binary which is not causing any harm to the system:

image19-Aug-04-2022-09-25-50-93-PM

Figure 10 - Rundll32.exe Process Tree as seen in the Cybereason Defense Platform (Non-Malicious)

 

In the above scenario, such a technique isn’t dangerous, but below we demonstrate how Rundll32.exe can be abused.

Note: For the following tests, we configured the EDR with features and options in "detection" mode only to demonstrate detection capabilities without immediately blocking the attacks ("prevention" mode).

Malicious Example #1 - LSASS Dump

However, Cybereason will – of course – detect malicious behaviors of rundll32.exe and trigger active MalOps when it is relevant. For example, we demonstrated a technique when adversaries abuse rundll32.exe and comsvcs.dll to dump LSASS memory. 

The Defender can leverage the MalOp™ to quickly ascertain all relevant information about the attack:

image11-Aug-04-2022-09-27-09-11-PM

Figure 11 - MalOp for Rundll32.exe dumping LSASS process memory as seen in the Cybereason Defense Platform

 

The Defender is then able to explore the process tree to hunt for suspicious activities:

image3-Aug-04-2022-09-28-26-04-PM

Figure 12 - Rundll32.exe Process Tree as seen in the Cybereason Defense Platform (Malicious)

 

Malicious Example #2 - Meterpreter Payload

To continue our examples, we could now create a malicious DLL using msfvenom to initiate a reverse shell to an adversary-controlled system. We will select a staged Reverse TCP Meterpreter payload and name it sample.dll:

image15-Aug-04-2022-09-29-17-43-PM

Figure 13 - Msfvenom - Meterpreter payload creation (DLL)

 

Let’s now execute the DLL using rundll32.exe:

rundll32.exe C:\Payloads\sample.dll,XYZ

 

A MalOp is created upon execution of this DLL to notify the Defender:

image8-Aug-04-2022-09-30-35-03-PM

Figure 14 - MalOp on “rundll32.exe” when loading a Meterpreter payload (DLL)

 

Note: The DLL is injecting a Meterpreter payload in Memory using Reflective DLL Injection technique, hence the reference to the “Module Injection” within the MalOp.

The Defender is well alerted and then able to explore the process tree to continue the hunt:

image20-Aug-04-2022-09-31-22-47-PM

Figure 15 - Process tree of rundll32.exe when loading a Meterpreter Payload (DLL) as seen in the Cybereason Defense Platform

 

The Defender can proceed with additional adversary hunting from the process tree where we notice floating modules which are indicators of Reflective DLL Injection (Delivering Meterpreter):

image16-Aug-04-2022-09-31-59-46-PM

Figure 16 - Meterpreter - Detection of floating modules

 

We notice a machine with IP 10.160.155.26 (victim) connecting to another machine with IP 10.160.201.220 on port TCP/8080. This corresponds to the reverse Meterpreter (TCP) payload we created (sample.dll) and executed using rundll32.exe:

image7-Aug-04-2022-09-32-49-37-PM

Figure 17 - Meterpreter - Connection to the attacker

 

Malicious Example #3 - VBScript Execution

Let's take a look at another technique to execute VBScript and spawn a PowerShell prompt:

rundll32.exe vbscript:"\..\mshtml,RunHTMLApplication "+String(CreateObject("WScript.Shell").Run("powershell.exe"),0)

 

Even though powershell.exe is a legitimate Windows binary, this indirect execution is still very unusual and suspicious because it could be used by adversaries as a roundabout execution means.

Thanks to our Behavioral Execution Protection feature, it is correctly marked as suspicious (unlike the cmd.exe and calc.exe executions we have previously seen, if you do remember):

image4-Aug-04-2022-09-34-33-88-PM

Figure 18 - MalOp on rundll32.exe “Malicious process” (Behavioral execution protection) as seen in the Cybereason Defense Platform

 

Hunting Queries 

The queries provided in this section can be used to hunt for possible malicious rundll32.exe processes. First of all, the following query provide all instances of rundll32.exe (including non-malicious ones), to have an overview of activities:

/#/s/search?queryString=0<-Process"elementDisplayName:)rundll32.exe"&viewDetails=false

 

Result:

image9-Aug-04-2022-09-35-30-12-PM

Figure 19 - [Hunt] List of all Rundll32.exe processes as seen in the Cybereason Defense Platform

 

Then, it is possible to refine and limit the search to suspicious occurrences, you can filter on Has Suspicion is True and/or Has Malops is True with the query below or using the user interface:

/#/s/search?queryString=0<-Process"elementDisplayName:)rundll32.exe
,hasSuspicions:true,hasMalops:true"&sorting=
elementDisplayName&sortingDirection=1&viewDetails=false

 

image23-Aug-04-2022-09-38-19-93-PM

Figure 20 - [Hunt] List of all Rundll32.exe processes with Suspicions/MalOps

 

It is also possible to extend your query and hunt for outgoing connections with the query below (or using the user interface to graphically build the query):

/#/s/search?queryString=2<-Process"elementDisplayName:)rundll32.exe,hasOutgoingConnection:true"->connections->remoteAddress&sorting=elementDisplayName&sortingDirection=1&viewDetails=false

 

Result:

image1-Aug-04-2022-09-36-40-89-PM

Figure 21 - [Hunt] Query creation - Rundll32 with outgoing connections as seen in the Cybereason Defense Platform

 

image14-Aug-04-2022-09-40-47-03-PM

Figure 22 - [Hunt] Results - Rundll32 with outgoing connections as seen in the Cybereason Defense Platform

 

We notice the remote IP 10.160.201.222 which is the adversary-controlled machine that received the Meterpreter reverse shell from the victim machine.

In the real world, this IP address could be used as a pivot (or “IoC” for indicator of Compromise) to pursue the Incident Response (IR) process and hunt for other malicious activities related to this specific address.

 

Cybereason is dedicated to teaming with Defenders to end attacks on the endpoint, across enterprise, to everywhere the battle is taking place. Learn more about the AI-driven Cybereason Defense Platform here or schedule a demo today to learn how your organization can benefit from an operation-centric approach to security.

About the Researcher:

Tristan MadaniTristan Madani is a perpetual learner and passionate about cybersecurity. He appreciates both defensive (GREM certified) and offensive security (OSCP, OSCE, GXPN certified). He has also worked as a security architect and more formerly as a network security engineer, system engineer and developer. When he is not working, he enjoys spending time with his family and going for walks in nature to recharge his batteries. Always looking for innovation and new challenges, he is delighted to work in Cybereason’s Blue Team, at the forefront of Cybersecurity and continue to learn in this exciting and ever-changing world.

Cybereason Blue Team
About the Author

Cybereason Blue Team

The Cybereason Blue Team is a global unit focused on mitigating advanced adversarial techniques leveraged by high level Threat Actors and Red Teams across the globe. The Blue Team is comprised of experts in Red Teaming, Penetration Testing, Digital Forensics and Incident Response, giving them a unique insight into both sides of the coin. Working alongside customers and third-party Red Teams, and leveraging the Cybereason platform, the Blue Team is able to push the boundaries of detection and response well beyond commonly known Tactics, Techniques and Procedures (TTPs), reversing the adversarial advantage long before new techniques are adopted by mainstream Threat Actors. As part of Cybereason Threat Intelligence, the Blue Team is able to quickly translate these findings into MalOp detections, ensuring our entire customer base is protected.

All Posts by Cybereason Blue Team