Wednesday, January 28, 2009

Conficker/Downadup: Memory Injection Model

The worm Conficker/Downadup does not need a special introduction as it was pretty well described in various write-ups in great detail.

Nevertheless, considering it employs a set of unique techniques, a deeper analysis is needed.

One of such techniques is its memory injection model that will be discussed in this post. Note: as the analysis continues, more posts might be expected.

Conficker runs as a DLL file and unpacks itself on the heap of the host executable process, such as rundll32.exe. In order to bypass firewalls (and possibly HIPS too), its author has certainly thought of a proper memory injection model.

The worm calls its memory injection function with the following prototype:

InjectIntoProcess (DWORD TargetProcessID, LPSTR ConfickerDllFilename)

The function is called for the processes explorer.exe, svchost.exe, and services.exe.

For start, the worm will open the target process, allocate a small memory region in its virtual address space, and write in it the full path filename of its own DLL.

Next, it will obtain the address of LoadLibraryA() API imported from kernel32.dll.

The worm will then make an interesting trick: it will call CreateRemoteThread() API by passing it the handle of the targeted process. The thread start address it specifies is the virtual address of LoadLibraryA() API (imported from kernel32.dll). The specified thread parameter is the address of the allocated buffer within the process where the full path filename of the Conficker DLL has just been written.

This will force the target process to spawn a thread that will load the worm DLL file – pretty neat, considering there is no executable code physically injected.

But that’s not all.

Following this trick, Conficker will enumerate all threads running inside the targeted process, and for every thread it will add to its queue an Asynchronous Procedure Call (APC).

For this purpose it uses an undocumented API NtQueueApcThread() which has the following prototype:

NtQueueApcThread(HANDLE hThreadHandle, PIO_APC_ROUTINE lpApcRoutine, PVOID pApcRoutineContext, ...),

where hThreadHandle is a handle of the enumerated thread that receives an APC call into its queue, lpApcRoutine is the address of the entry point to the user APC routine, and pApcRoutineContext is the user defined parameter for APC routine.

Guess what address it uses for the user APC routine? That’s right, it’s the address of the API LoadLibraryExA() imported from kernel32.dll, and the parameter for this call is the name of the Conficker DLL, previously saved inside the process’s address space.

With the APC queued for the target process threads, as soon those threads are signaled, the routine that loads Conficker DLL will be invoked. Hence, the remote injection.