More hidden Phantom DLLs

Pretty much all of the phantom DLL scenarios that I have been describing over the years are linked to specific use cases, where the code referencing these non-existing DLLs is most of the time immediately accessible from a native OS program. And as such, the technique can be leveraged immediately. In other words, anyone with some basic know-how about phantom DLLs can control the process of loading payloads leveraging this trick at any time.

The complexity of Windows OS cannot be understated.

Despite many efforts, hours spent reversing, I often come across situations where I simply don’t understand the code, the logic, or the conditions to load certain phantom DLLs are so convoluted, that it is simply not worth inspecting any further….

This is where this post begins.

There are still many phantom DLLs that I have not described yet, but the chances that I ever will are not very high. This is because most of the ‘obvious’ phantom DLLs are already pretty well-described, and the effort that one has to make to describe the ones that are left is ‘uuuuge. But… there is an easier way.

Instead of chasing unicorns, we can just list the possible scenarios w/o going into details on how they are invoked. From a defender’s perspective it is still a win, because we can simply focus on detection of phantom DLL files being created, without a need to understand how they will be loaded by threat actors.

Okay…

That’s a lot of words to describe never-seen-before IOCs.

So, here they are.

When the files are created that match the file names in the column 2, it is worth investigating further…

A silly rundll-ish feature of ShellAbout function…

When you run winver it calls the shell32.dll!ShellAbout function to display the following dialog box:

It turns out the ShellAbout function’s declaration makes it a potential target for calling it from rundll32.exe, even if its prototype doesn’t follow the rundll32 calling protocol.

The function accepts the following parameters:

INT ShellAboutA(
  [in, optional] HWND   hWnd,
  [in]           LPCSTR szApp,
  [in, optional] LPCSTR szOtherStuff,
  [in, optional] HICON  hIcon
);

and the rundll32 callback is declared as:

RunDll32EntryPoint(
HWND hwnd, 
HINSTANCE hinst, 
LPSTR lpszCmdLine, 
int nCmdShow
);

In other words, the mapping of the function arguments looks like this:

HWND   hWnd -> HWND hwnd
LPCSTR szApp -> HINSTANCE hinst
LPCSTR szOtherStuff -> lpszCmdLine
HICON  hIcon -> nCmdShow

So, by calling:

rundll32.exe shell32.dll, ShellAbout Visit http://extend-windows-license-by-10-years-for-free.com now!

we fill-in the following bit:

LPCSTR szOtherStuff -> lpszCmdLine

with a string provided via a command line.

And as the API documentation describes, this parameter is:

A pointer to a null-terminated string that contains text to be displayed in the dialog box after the version and copyright information. This parameter can be NULL.

Thanks to that coincidence, the result of our rundl32 invocation is the following dialog box:

If you paid attention, you probably noticed that the title of the dialog box got corrupted:

but that’s a side-effect of szApp parameter getting some random value from the stack/rdx register (if you follow the calling conventions of x86/x64).

Rest assured that this is not a security risk, but just yet another example of using Windows API in a slightly unorthodox way, similar to this example I posted about a few years back…