With the recent KeePass2 disputed CVE-2023-24055 and all the fuss around it, it motivated me to finish a little project I had started last year.

My goal was to see if I could find a way to intercept the Master Password of a KeePass2 database.
For fun and learning ofc.

In this article, we’ll cover:

  • DLL Hijacking
  • Hooking Windows APIs

Introduction

Picture this.
You are on an engagement, admin on a workstation and just now retrieved a KeePass2 Database.

Problem: KeeFarce/KeeThief don’t work anymore.

Thanks obama

 

Let’s find another way to get that Master Password.

Table of content

DLL Hijacking

Was ist das?

DLL hijacking is a type of attack where you take advantage of an application’s search order for loading dynamic-link libraries.

When an application attempts to load a DLL file, it will search for the file in a specific order. The order is as follows:

  • The directory from which the application loaded
  • The system directory: C:\Windows\System32
  • The 16-bit system directory: C:\Windows\System
  • The Windows directory: C:\Windows
  • The current directory
  • Directories specified in the PATH environment variable

If an attacker is able to place a malicious DLL file in one of these directories with the same name as a legitimate DLL file, the application will load the malicious DLL instead of the legitimate one, allowing the attacker to execute arbitrary code in the process.

Does this also apply to KeePass?

YES

Target DLL

The easiest way to find potential hijackable DLL is to search with promon. Search for CreateFile on a DLL that returns with the error NAME NOT FOUND, such as this one here:

procmon speleology

What is happening?
Here KeePass tries to a DLL called UxTheme.dll, but tries to load it for its own installation folder in C:\Program Files\KeePass Password Safe 2.
However, this is normally a system DLL and is present in C:\Windows\System32:

UxTheme.dll in System32

Thus, this DLL might be a good candidate for hijacking.

Let’s compile a DLL and try to see if it gets loaded in KeePass2 if we rename it. We’ll use this code:

BOOL APIENTRY DllMain(
    HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    WPP_INIT_TRACING(L"Test");

    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        TraceEvents(TRACE_LEVEL_VERBOSE, GENERAL, "[+ dllmain] DLL_PROCESS_ATTACH\n");
    }
    return TRUE;
}

Notice the call to TraceEvents instead of a printf. This is because as we are working with a DLL, there is no easy way to get its output.

TraceEvents uses the Windows software trace preprocessor (WPP), that is a component of ETW.
We’ll be able to see our events in TraceView.

Now that we have our DLL, let’s move it to C:\Program Files\KeePass Password Safe 2 and rename it to UxTheme.dll:

Dropping the DLL in KeePass' folder

And run KeePass:

DLL_PROCESS_ATTACH when launching KeePass2

Yeah!! It worked. We have a log in TraceView showing our DLL loaded in KeePass2.

That was easy. Now what?

Hooking Windows APIs

Hooking allows you to intercept and/or modify the behavior of functions called by a given program. In our case, we want to be able to log parameters and return values of Windows API calls.

In the following diagram, you can see a normal API call (in green) versus a hooked API call (in red):

Hooked call

Fun fact, that is also how userland AV/EDRs monitor API calls.

I started working on a custom hooking engine, before stumbling upon Microsoft’s own library to do precisely that: Detours

I won’t go into details on the implementation of the hooks as documentation is available online.

MessageBoxW Example

Let’s start simple with a MessageBoxW call.

MessageBoxW(
    NULL,
    TEXT("Hello Twitter!"),
    TEXT("SimpleEXE"),
    MB_OK
);

If we take a look at the documentation for MessageBoxW on MSDN, we get this function prototype:

int MessageBoxW(
  [in, optional] HWND    hWnd,
  [in, optional] LPCWSTR lpText,
  [in, optional] LPCWSTR lpCaption,
  [in]           UINT    uType
);

Our hook function to log the lpText and lpCaption parameter would look something like this:

int (*real_MessageBoxW)(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType) = MessageBoxW;
int hook_MessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType)
{
    TraceEvents(TRACE_LEVEL_VERBOSE, GENERAL, "[+ Hook] MessageBoxW(lpText=%ls, lpCaption=%ls, uType=%u)", lpText, lpCaption, uType);
    return real_MessageBoxW(hWnd, lpText, lpCaption, uType);
}

This code is pretty simple. When MessageBoxW is called, it will first log the arguments using TraceEvents before resuming execution to the “real” MessageBoxW.

Let’s try it out!

Hook the box

This was tested with a custom loader that injects the DLL when starting the process. And when the MessageBox appears:

Logging the password in traceview

The function call gets logged in TraceView! With the arguments and all.

Now, imagine being able to run code in a sensitive process and being able to intercept functions that handle sensitive data.

Automatic code generation

But first, code generation.

Now that we have a first working example of hooking, we have to scale it up. There are a lot of functions in the Windows API. Remember, our objective is to intercept the Master Password of the database, when it is typed.

I’m clearly not writing the code for each Windows API function myself. Ain’t nobody got time for that. I went for a Python script and a Json file to generate the code for the hooking dll.

MSDN is your best friend for documentation, or you could also check header files in Visual Studio.
Here is a quick pick of the json file:

Json file with fuction prototypes

And the generation:

DLL generation output

The default code generation invokes TraceEvents to log function calls and arguments. Here is a sample of the generated code:

Generated code

It looks a lot like the code in our first example but is generated automatically, as long as the function is added to the json file. I also added custom code snippets to extend functionalities if necessary. That way we can log return values, print arguments in a specific way, etc.

And just like that, we have working code generation. We can monitor any Windows API function that we want by adding it’s prototype to the json file!

When everything comes together

While testing, I narrowed my search to function that handle strings and clipboard activity. After a bit of research I found two interesting API calls for our use case:

  • SetClipboardData: Places data on the clipboard in a specified clipboard format
    HANDLE SetClipboardData(
    [in]           UINT   uFormat,
    [in, optional] HANDLE hMem
    );
    
  • ToUnicodeEx: Translates the specified virtual-key code and keyboard state to the corresponding Unicode character or characters
    int ToUnicodeEx(
    [in]           UINT       wVirtKey,
    [in]           UINT       wScanCode,
    [in]           const BYTE *lpKeyState,
    [out]          LPWSTR     pwszBuff,
    [in]           int        cchBuff,
    [in]           UINT       wFlags,
    [in, optional] HKL        dwhkl
    );
    

After generating hooks for those two functions, we copy the DLL to C:\Program Files\KeePass Password Safe 2 and rename it to UxTheme.dll.

And now, let’s run KeePass2 again and type the password to unlock the database:

Logging the password in traceview

How cool is that? The password gets logged in TraceView!

Entries in the database can also be intercepted when CTRL+C is pressed, thanks to the hook on SetClipboardData:

Clipboard hook

Conclusion

I still have some work to do to write the password to a file but you got the idea. Hope you liked what you read and learned something new today.

I don’t have a comment section but send me a message over on Twitter before the platform disappears haha.

Useful Links

https://keepass.info/

https://learn.microsoft.com/en-us/sysinternals/

https://github.com/microsoft/Detours

https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/traceview https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/wpp-software-tracing