Malware Persistence without the Windows Registry
For an attacker to maintain a foothold inside your network they will typically install a piece of backdoor malware on at least one of your systems. The malware needs to be installed persistently, meaning that it will remain active in the event of a reboot. Most persistence techniques on a Microsoft Windows platform involve the use of the Registry. Notable exceptions include the Startup Folder and trojanizing system binaries. Examining malware persistence locations in the Windows Registry and startup locations is a common technique employed by forensic investigators to identify malware on a host. Each persistence technique commonly seen today leaves a forensic footprint which can be easily collected using most forensic software on the market.
The persistence technique I’ll describe here is special in that it doesn’t leave an easy forensic trail behind. A malware DLL can be made persistent on a Windows host by simply residing in a specific directory with a specific name, with no trace evidence in the registry or startup folder and no modified system binaries. There isn’t just one directory location and DLL filename that are candidate locations for this persistence mechanism but rather a whole class of candidate locations exist for any given system. On my laptop (Windows 7 64-bit) there are no less than 1032 such path and DLL name combinations where a DLL could be placed such that it would automatically be loaded at some point during my normal boot-up, and that’s just for a 32-bit DLL! If you had a 64-bit malware DLL the number would be much higher as I have many more 64-bit processes running at boot time. So how does this work?
DLL Search Order Hijacking
When an application requests to load a DLL either statically via an import table in its executable file, or dynamically via the LoadLibrary() function the operating system will look for the DLL in a predefined sequence of locations. This sequence is defined in the MSDN documentation here: http://msdn.microsoft.com/en-us/library/ms682586(VS.85).aspx. The most important tidbit of information to take away from that document is that the first place the application looks for a DLL is the location of the executable itself. This isn’t always the case though. If the DLL name that is requested is listed in the “\\.\KnownDlls” object then it will always load from a fixed location (the System32 folder). This object is populated at boot-time using data from the Registry at the following location:
Microsoft employee Larry Osterman describes this in a blog post (http://blogs.msdn.com/b/larryosterman/archive/2004/07/19/187752.aspx). He states in the post that the KnownDlls object will be larger in memory than what is in the Registry key and will be built recursively from the statically imported DLLs from any DLL listed in the registry. In the limited testing I’ve done on Windows XP and Windows 7 systems, the KnownDlls object in memory is identical to the list provided by the KnownDLLs registry key.
Casual browsing of the KnownDlls key will reveal a short list of about 30-35 of the most commonly used DLLs. For example, the low level networking API DLL “ws2_32.dll” is contained in this list. Whenever any application attempts to load a DLL named “ws2_32.dll” it will always load it from the System32 folder because it is listed in this key, regardless of where the application was launched from. The KnownDlls system provides a thin layer of security for this small set of crticial DLLs because an attacker can’t simply place a DLL named “ws2_32.dll” inside a folder containing an application which uses ws2_32.dll and expect their local copy to be loaded. The KnownDlls system is far too limited to provide any realistic sense of DLL loading security though. For example, even though we can guarantee that the copy of ws2_32.dll that will be loaded will always be the one from system32, other components loaded when ws2_32.dll is loaded (such as iphlpapi.dll and mswsock.dll) are not guaranteed because they are not covered by KnownDlls.
Lets imagine that we had a legitimate program called update.exe which ran from the location “C:\Program Files\MyCompany” and loaded ws2_32.dll, all we would have to do to make update.exe load our malware DLL is place our malware in the “C:\Program Files\MyCompany” directory and give it the name “iphlpapi.dll”. When the update.exe program runs it loads ws2_32.dll, which in turn loads iphlpapi.dll which it loads from the application directory first before checking the System32 folder where it legitimately resides. All the malware author needs to do is make sure their malicious iphlpapi.dll eventually loads the real thing and the user of the system (and a forensic analyst most likely) will have no idea that malware has been loaded.
You might have come to the conclusion in reading the description of the problem above that executables which reside in the System32 folder are not susceptible. If you thought that, you’d be correct. If you also thought that there is no real practical problem because all consistent and reliably placed startup binaries exist in the System32 folder, you’d be incorrect. Case-in-point: Explorer.exe . Strangely, this binary resides in C:\Windows (I assume for historic reasons). So when explorer.exe launches and it requests a DLL that is not protected by KnownDlls, the first place the system looks to find the DLL is the C:\Windows directory. Thus far, the most common place we’ve found this malware persistence technique being used is in the location and name “C:\Windows\ntshrui.dll”. The real ntshrui.dll is located in the System32 folder but since this dll is loaded by Explorer.exe and not protected by KnownDlls, it’s unfortunately susceptible to DLL search order hijacking.
The Extent of the Problem
Once you really understand the nature of the problem it may occur to you that it’s a very widespread and pervasive issue. It has always existed in Windows and will likely exist for the foreseeable future. To alter the DLL search path mechanism could have severe backward-compatibility problems for Windows and is most likely not going to happen due to the high value they have always placed in compatibility (We love you Raymond Chen!). I’ve written a program to identify all locations and filenames that a DLL could be placed to achieve persistence on a given system. The idea is that you can run this program on a clean (Gold Image) system and forensically search for any DLL name listed in the output on a machine you suspect of being compromised with this method of persistence. Similar programs may be developed to attempt to identify hijacked DLLs on a live system. I chose to write this program first however because its output helps to explain the extent of the problem. I ran the program on my laptop and it produced output which contained 1032 lines, each describing a location and filename that a DLL could be placed to be loaded at boot-time by my system. On a clean XP SP2 machine I get 91 locations listed. Here are a few lines from the output from my laptop:
Hijackable Location: C:\Program Files (x86)\iTunes\SspiCli.dll
Hijackable Location: C:\Program Files (x86)\iTunes\CRYPTBASE.dll
Hijackable Location: C:\Program Files (x86)\iTunes\CoreFoundation.dll
Hijackable Location: C:\Program Files (x86)\iTunes\MSVCR80.dll
According to this output, some program that loads when my system boots (most likely iTunes) attempts to load the DLL named “CRYPTBASE.DLL” which is commonly found in the System32 folder but an attacker could place a malicious DLL in the iTunes folder and that would be loaded instead. The program examines running processes and determines hijackable DLL locations by the following properties (applied to each loaded dll in every running process in the system):
- The process executable that loaded the DLL is not located in the System32 folder
- The DLL name is not found in the KnownDlls object
- The DLL is not found in the same directory as the executable
Any loaded DLL that contains all three properties is susceptible to being trumped by search order hijacking.
The tool (compiled and source) to identify possibly malicious 32-bit DLL locations from a clean system can be found here.