|
Bypassing ServerLock protection on Windows 2000 Jan K. Rutkowski <jkrutkowski@elka.pw.edu.pl> 1. Background ServerLock for Windows 2000 is product of Watch Guard company. The purpose of this tool is to protect integrity of the operating system by ensuring that nobody can modify certain files (like DLLs or drivers), some registry keys and load unknown drivers. Watch Guard homepage: http://www.watchguard.com. There are two bugs in the recent version of Server Lock for Windows 2000 which can be exploited to completely bypass its protection. 2. Bug #1 - loading arbitrary modules (using Runtime Process Infection) 2.1. Basics It is possible to circumvent ServerLock protection, so that attacker can load arbitrary module into kernel. Normally such action is restricted by ServerLock, because: a) it is not possible to create keys under HKLM\System\CurrentControlSet\Services, so official Windows 2000 mechanism to load drivers cannot be used (SCM and ZwLoadDriver() which is used by SCM, require that each driver has a dedicated subkey under the 'services' key in the registry). b) it is not possible to replace a 'legal' driver (files form \Winnt\System32\drivers directory), because ServerLock protects these files from write access. c) there exists undocumented function: ZwSetSystemInformation(), which can be used to load driver, and this function doesn't require any keys in the registry. ServerLock protects these function, so that only 'trusted' programs can use it. The method of attack, presented below, assumes ServerLock 2.0.1 for Windows 2000. Prior versions have not been tested, but it is likely that they are also vulnerable. 2.2. DLL injection First of all, we should explain what 'trusted' program means. Every EXE file, which is guarded (i.e. modification is not allowed) by ServerLock is considered 'trusted'. The reason for this, is that attacker cannot change the trusted program's code. However this is not true, when we are considering running process. Attacker can open process object by means of OpenProcess() function, and then can use for example CreateRemoteThread() in order to inject DLL library of his choice into the target process address space. This is called 'DLL injection' and has been researched years ago, and can look like this (simplified): CreateRemoteThread ( hTargetProc, ... addr_of_LoadLibrary, addr_of_"bad.dll"_in_target_process, ... ); Of course before that, one must ensure that "bad.dll" string is present in the target process address space. This can be done by means of VirtualAllocEx() and WriteProcessMemory() functions. As we all know, just after DLL is loaded into the process memory, DLL's entry point, usually called DllMain, is executed. We see, that attacker, with proper privileges, can supply arbitrary instructions to 'trusted' process and these instructions will be executed. It should be noted that DLL injection is not the only technique to achieve this runtime process infection, but is the simplest one. Other techniques do not requires creation of additional file ("bad.dll") in the filesystem (which is not very stealthy). 2.3. Calling ZwSetSystemInformation() Assuming that attacker's rootkit resides in file rootkit.sys, we can copy this file into \WINNT\system32\drivers\ directory as a rootkit.abc for example. ServerLock forbids creation only files with some known extensions like .sys, but for ZwSetSystemInformation() function extension doesn't matter. Using DLL injection technique (described above) we can force any 'trusted' process to execute ZwSetSystemInformation() (SystemLoadAndCallImage class), thus allowing attacker to load his rootkit.abc module into kernel. 2.4. Other usage of OpenProcess() Despite exploiting ZwSetSystemInfomration(), OpenProcess() function can be used to do API hooking in other processes. This future is used by some recently popular Windows usermode rootkis, like hxdef. It would be a good idea to restrict OpenProcess() then. 3. Bug #2 - improper handling of symlinks to \Device\PhysicalMemory Server Lock is trying to restrict access to \Device\PhysicalMemory to disallow accessing kernel memory. However it is possible to create some symlinks to this object and then, open this device via symlink. The following output demonstrates just few possibilities: C:\>funWithLinks.exe creating link: \hak1 --> \Device creating link: \hak2 --> \Device\PhysicalMemory creating link: \hak3 --> \ creating link: \hak4 --> [failed] creating link: \Device\hak5 --> \Device creating link: \Device\hak6 --> \??\GLOBALROOT trying to open for READ|WRITE: opening \Device\PhysicalMemory ... [failed] opening \hak1\PhysicalMemory ... [it worked!] opening \hak2 ... [it worked!] opening \Device\hak5\PhysicalMemory ... [it worked!] opening \Device\hak5\hak5\PhysicalMemory ... [it worked!] opening \??\GLOBALROOT\Device\PhysicalMemory ... [it worked!] opening \Device\hak6\hak1\PhysicalMemory ... [it worked!] It should be obvious that having possibility to open \Device\PhysicalMemory for writing we can do whatever we want with the system, like loading some rootkits and backdoors. It means complete system compromise. Although this is not trivial, since we must translate linear addresses to physical, proof-of-concept codes can be found on the net. 4. Vendor response 17.02.2003: bug 1 has been reported to Watch Guard 10.03.2003: bug 2 has been reported to Watch Guard 28.03.2003: SL 2.0.3 has been released which fixed bug #1 02.07.2003: SL 2.0.4 has been released which fixed bug #2 SL 2.0.3 does not restrict OpenProcess(), but SL 2.0.4 does, which should stop Runtime Process Infection efforts. 5. Epilog These bugs have been fixed and a patch made available to all current subscribers on vendor's LiveSecurity Web site at: https://www.watchguard.com/archive/softwarecenter.asp Watch Guard refused to provide details about how they fixed the bugs.