Managed DLL Exports and run via rundll32

Hello All,

Security landscape is changing very quickly wherein security researchers have to be more dynamic and innovative to improve the security posture of the organizations.

In this post I will cover the concept on how we can develop a DLL with exported function in C#.
Note:- I am not the first person to develop this tool or technique.

Thanks to Nikhil Mittal for giving me the idea and encouraging me to develop and play with Exported DLL functions in C#. Also thanks to Adam Chester for writing an amazing blog post on this technique.

What is DLL ?

Dynamic Link Library (DLL) is a file which contains the code and is been loaded by the program to perform one or more actions during run time.

Introduction

DLL files are very commonly used during red teaming exercise or while simulating attacks / threat actors. There are multiple ways by which we can execute the arbitrary DLL's. LOLBAS project has documented most of those techniques which can also be used for bypassing Applocker. Many organizations don't enable Applocker rules for DLL due to performance issue warning displayed on the Applocker window while enabling the Applocker rules for DLLs. Hence DLL based execution becomes important primitive during such assessments.

Demo

In the demo we will look at how we can modify seatbelt project to be executed with rundll32 and also look at how we can execute covenant grunt using rundll32.

Before we look at the demo follow the below mentioned steps for modifying or creating any new project in c# with Exported DLL functionality.

Configuration level changes
  1. Open Visual Studio application and create new project or load existing project. 
  2. Go to Project tab and open the Properties window
  3. Change the target framework setting to .Net Framework 4
  4. Change the Output type setting to Class Library
  5. Go to Build tab and open the Configuration Manager window
  6. Change the Platform setting from Any CPU to the architecture on which you want to run the application like x64 or x86
  7. Go to Tools tab and click on NuGet Package Manager and open the Manage NuGet Packages for Solution window
  8. In Browse window search for DllExport library and install the DllExport by github.com/3F/DllExport library.
  9. Once install you will get a pop-up stating to remove the nuget package before continuing we need to Uninstall the library. While uninstalling you will get an pop-up where it will ask us to restart visual studio.
  10. Open the folder where the project files are located. The folder will contains DllExport.bat file. 
  11. Double click on the DllExport.bat file. .Net DllExport window will be opened. 
  12. Click on Installed checkbox and select the architecture radio button on which you will be running the application like x64 or x86 and then click on Apply button.
  13. Click back in visual studio window and it will pop-up a message window to reload the project. Click on Reload All button.
Code level changes
  1. Modify the Main(string[] args) function of the .net code like the below example
# Default Main function
static void Main(string[] args)

# Modified function name
public static void function()
  1. Add the DllExport attributes for the function
[ProjectName.DllExport("function", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall)]
public static void function()
  1. To get the command line output in the same console we need to call AttachConsole function
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
  1. Call the AttachConsole function in the function as the initial statement
const uint ATTACH_PARENT_PROCESS = 0x0ffffffff; // This is used to bind the output to the parent console
AttachConsole(ATTACH_PARENT_PROCESS);
  1. To pass the command line arguments in the program we will use GetCommandLineA function
[DllImport("kernel32.dll", SetLastError = true)]
static extern string GetCommandLineA();
  1. Code to extract only the arguments from command line
string cmdval = GetCommandLineA(); // To retrieve command line string for current process 
string fname = "function"; // Function name which is exported
int funcval = cmdval.IndexOf(fname); // Find the index value of the function from the command line
int fargstartindx = funcval + fname.Length; // Find the argument starting index value
string finalargs = cmdval.Substring(fargstartindx); // Copy the arguments
string[] args = finalargs.Split(' '); // Split the arguments using space

Seatbelt Demo

Seatbelt is a C# project that performs a number of security oriented host-survey "safety checks" relevant from both offensive and defensive security perspectives.


Covenant Demo

Covenant is a .NET command and control framework that aims to highlight the attack surface of .NET, make the use of offensive .NET tradecraft easier, and serve as a collaborative command and control platform for red teamers.


The exercise for the reader is to follow the steps mentioned above and create your own tools or customize an existing tools.

Detection

Use process monitoring to monitor the execution and arguments of rundll32.exe. Compare recent invocations of rundll32.exe with prior history of known good arguments and loaded DLLs to determine anomalous and potentially adversarial activity. Command arguments used with the rundll32.exe invocation may also be useful in determining the origin and purpose of the DLL being loaded.
Also monitor dll's which are not loaded from the  default path (c:\Windows\System32\* or c:\Windows\SysWOW64\*) for abnormal behavior such as opening network connections.

References

https://blog.xpnsec.com/rundll32-your-dotnet/
https://github.com/3F/DllExport

Thanks for reading the post.

Special thanks to all my friends who help / supported / motivated me for writing blogs. 🙏

Comments

Popular posts from this blog

Process Injection - Part I

Introduction to Callidus

Exploring the Dark Side of Package Files and Storage Account Abuse