Home | Resources | Newsletters | VICE | VI20051104Vice Newsletter - November 4, 2005Welcome on behalf of the eEye Research Team to another issue of VICE, a free technical newsletter featuring content from the team representing the foundation of eEye as a company and culture.
With this issue, we offer part two of Derek Soeder's research on how Windows hotkeys operate. This month, he puts everything together to highlight an application of the concepts featuring a tool called eEye SysReq that pops a system shell with a hotkey combination involving the often-overlooked SysRq key.
As always, we have reviewed the questions we received from our readers and our research team has provided expert comments and answers. Finally, we end the month with the cease-and-desist letter from a software company that had their source code posted publicly, followed by a definition and clarification of the legalese used in the often-debated issue of the legality of reverse engineering.
Enjoy!
Christopher Imes, EditorIn This IssueVulnerability Exposed!The Making of eEye SysReq: Bootstrap Code for a Local SYSTEM Shell HotkeyIn a previous article, we thoroughly explored how Windows processes its "special" hotkeys, and found that the responsibility was split between WIN32K.SYS and the WINLOGON.EXE process. This time, we'll put the effort to good use in an adaptation of eEye BootRootKit dubbed "SysRq".
For more information on the eEye BootRoot project, please refer to the presentation slides available at:
http://www.eeye.com/html/resources/downloads/other/index.html.
SysRq (pronounced "Sys Request", like the keyboard button) comprises one kilobyte of bootstrap code that entrenches itself in the Windows kernel as it loads, and once there, it registers a special hotkey (Control + Shift + SysRq) that will open a command shell with full SYSTEM-level privileges. Although it would serve as an intensely useful system administrator's tool, it of course facilitates illegitimate administrative access as well, so we are currently undecided on whether or not to release it, in source or binary form. However, the project is a technically engaging one, so it is our hope that at least discussing the technical details of its implementation will be interesting and useful to our readers. Knowledge of low-level Windows kernel- and user-mode programming is recommended.
SysRq OverviewThe requirements for SysRq are simple: it should be boot sector code that causes a command prompt running as SYSTEM to appear whenever a specific hotkey is pressed. Because it's bootstrap code, it will necessarily need to reside in the Windows kernel, but spawning "cmd.exe" is most easily done from user-land. Because a lot of hotkey processing is done by Winlogon, and because it runs as SYSTEM and is responsible for starting other interactive processes (like Task Manager), it seems like the most suitable host process for such user-mode code.
As we've mentioned before, SharedUserData at FFDF0000h / 7FFE0000h is the boilerplate way to bridge kernel space and user space, and in fact we will use it to host our user-mode code so that we can avoid the burden of locating the Winlogon process and writing into its address space.
The actual technique we use to invoke this code, however, was subject to revision throughout the term of the project. The implementation in SysRq version 1.0 caused blue-screens on Windows XP and later, necessitating a second version which is universal and is perhaps a more elegant solution anyway. However, even the failed attempt may still deserve a look for educational purposes, so we will now cover the technical details of both.
SysRq v1.0: Windows 2000 OnlyThe first stages of SysRq are identical to those of BootRootKit, basically:
- Install an INT 13h (BIOS disk services) hook,
- patch OSLOADER.EXE as it loads, and
- patch the Windows kernel once it's in memory.
The problem at the third step is that we only have loaded-module memory as reliably addressable storage. BootRootKit overwrote the DOS code in between the MZ and PE headers of NDIS.SYS, because it had less than 40h bytes to plant in the kernel, but the persistent code for SysRq is much larger so our approach will require some adjustment.
SysRq v1.0 employs the same technique in order to install a small PspInitializeSystemDll hook function in NTOSKRNL.EXE's DOS code, but this is only the first step. The hook function code temporarily maps in the page of conventional memory containing the entirety of SysRq at virtual address 0, by modifying the first entry of the first page table at fixed virtual address C0000000h. It copies SysRq into an unused portion of SharedUserData at FFDF0800h, then continues execution there in order to perform the original function of the code displaced by our hook.
Our goal is to replace the address of NTDLL.DLL!LdrInitializeThunk, as it is known to the kernel, with that of our own "initialization" code that will reside in each process within the user-land view of SharedUserData (at 7FFE0000h). NTOSKRNL's initialization code obtains this address by calling PspLookupSystemDllEntryPoint with the string "LdrInitializeThunk". We find this CALL instruction by searching for references to this string, then hook the CALL itself rather than the PspLookupSystemDllEntryPoint function, as we only want to supplant LdrInitializeThunk. (Other NTDLL exports are located using this function as well, including KiUserApcDispatcher and KiUserExceptionDispatcher.)
The hook passes the original function call through, then replaces the returned LdrInitializeThunk function pointer with a pointer to our code in SharedUserData. Of course, we must keep the original pointer for subsequent invocation, once our custom "initialization" code has finished.
This code constitutes the core of SysRq. Each new thread will begin execution there via an Asynchronous Procedure Call, which is perfect because we want our code to execute in each process but only have an effect within Winlogon - a simpler approach than trying to specifically target Winlogon from within the kernel. However, we only want our code to run at process initialization, rather than at every thread's initialization, yet we cannot unhook it from within user mode. An easy solution here is to check the loader data ("Ldr") field at [PEB+0Ch]; if it is non-NULL, then the process has already been initialized, and our code will not run any further.
When reached before process initialization, our code continues on to search the application image for the Unicode string "SAS window class", and if it's found, then we search for references to that string to find the RegisterClassW call that creates the class. At that point, we can locate SASWndProc - the class's window procedure - by retrieving the address stored at WNDCLASS.lpfnWndProc before the call is made, then hook it using typical function entry-point modification.
Our SASWndProc hook function is constructed to be as simple as possible - we assume that the instructions we're replacing are "PUSH EBP / MOV EBP, ESP / SUB ESP, (value <= 800h)", although a check to skip over an initial "MOV EDI, EDI" is necessary for recent Service Packs like XP SP2. In order to install the hook, we make the code writable using ZwProtectVirtualMemory, then plant a relative CALL over the function's prolog to give ourselves first dibs on messages bound for the "SAS window".
The hook function behaves like a very basic SASWndProc, in that we register our hotkey with USER32.DLL!RegisterHotKey upon receiving the WM_CREATE message, and handle that hotkey when a WM_HOTKEY message arrives bearing the special 'idHotKey' value we specified. Obviously, we'll need USER32.DLL's base address in order to look up RegisterHotKey, but it can be obtained easily by using the 'User32DispatchRoutine' pointer at [PEB+2Ch] as a hint and walking backwards until we find the MZ and PE headers.
When the Ctrl+Shift+SysRq hotkey is pressed, our WM_HOTKEY handler basically just creates a "cmd" process and returns directly to SASWndProc's caller, short-circuiting the original function entirely. In practice, however, a handful of API calls are involved in getting this to work, mostly to get the current window station and desktop so we can have a command shell even on locked or pre-login systems. In order, they are:
- GetProcessWindowStation()
- GetUserObjectInformationA(hWinSta, UOI_NAME)
- OpenInputDesktop(0, FALSE, 0)
- GetUserObjectInformationA(hDesktop, UOI_NAME)
- CloseDesktop()
Once we plug the constructed "WindowStation\Desktop" string into the 'lpDesktop' field of a STARTUPINFO structure, we call CreateProcessA, clean up with CloseHandle, and we're done. The user should now have a SYSTEM command shell.
As we mentioned before, though, this method for piggybacking Winlogon fails disastrously on Windows XP and later. Apparently Windows Product Activation, which is housed in Winlogon, verifies the integrity of the WINLOGON.EXE in-memory image and terminates the process - which crashes the system with a blue screen - if tampering has occurred. Although we could attempt to attack or deceive that code, or choose a different hook target that would allow us to unhook before our code changes were perceived, we instead pursued a very different approach to accomplishing the same goals, and the result is SysRq version 2.0.
SysRq v2.0: Windows 2000, XP, and 2003Like BootRootKit, version 2.0 of SysRq also installs an INT 13h hook at boot time in order to patch OSLOADER.EXE, and from there infiltrates the Windows kernel. Like SysRq v1.0, it seeks to subvert Winlogon's SASWndProc as well, but the path it takes between the two points varies significantly from that of its predecessor. Knowing that we cannot modify the WINLOGON.EXE image, we instead attack the window class registration mechanism itself.
USER32.DLL's RegisterClassW is a wrapper around the function RegisterClassExWOWW, which makes some preparations and eventually calls NtUserRegisterClassExWOW, a User32 function that issues a system call to invoke the WIN32K.SYS function of the same name. Since SysRq initially runs in the kernel anyway, it would seem straight-forward to hook WIN32K.SYS, but at the time our kernel code is executing, not even NTOSKRNL has initialized yet, and we're still stages away from the point where the SMSS.EXE process loads WIN32K.SYS.
The solution to this problem is simpler than it sounds. When WIN32K.SYS initializes, it calls KeAddSystemServiceTable to register the User and GDI system routines it provides. By hooking that function, we're poised to execute as soon as WIN32K.SYS has loaded, and we'll be supplied with pointers to its system service table (_W32pServiceTable) and corresponding argument count list (_W32pArgumentTable) free of charge. Like SysRq v1.0, we use this hook to execute a small "stub" placed in the DOS code of NTOSKRNL's header, which then copies the rest of the SysRq code into SharedUserData and resumes execution there.
The remainder of our KeAddSystemServiceTable hook function is concerned with hooking the NtUserRegisterClassExWOW system call, a slightly complicated process since the function is not exported. Our answer is to scan the code of each function in _W32pServiceTable until we find NtUserRegisterHotKey, which was chosen only coincidentally because of the easily identified code signature "FFFF7FF0h" that it uses to check for prohibited modifier flags. For safety, we also require the function to accept 10h bytes of arguments according to its entry in _W32pArgumentTable, since we know that NtUserRegisterHotKey always takes four arguments.
Knowing the location of this function in the system service table, we then search backwards for NtUserRegisterClassExWOW on the premise that the functions are organized alphabetically. The easiest way to know when we've encountered it is by its distinctly high number of arguments, either 6 or 7 (18h or 1Ch bytes) depending on the Windows version. Once found, we replace its entry in the WIN32K system service table with a pointer to our own hook function, store the original function pointer, and unhook from and resume execution of KeAddSystemServiceTable.
The NtUserRegisterClassExWOW hook function doesn't do anything sophisticated; it just compares the class name (the second argument, a UNICODE_STRING pointer) to "SAS window class", and in the event of a match, it replaces the incoming WNDCLASSEXW structure's 'lpfnWndProc' field with our replacement SASWndProc code within the user mapping of SharedUserData, after storing the original function pointer in the PEB at a specific location. We do perform input parameter validation - after all, we wouldn't want to introduce a local kernel vulnerability - but the details of that are uninteresting.
Finally, the surrogate SASWndProc function behaves identically to that of SysRq v1.0, except for the necessary differences required to transfer execution into the original SASWndProc. The end-result is again a SYSTEM shell on demand.
The SysRq v2.0 approach has one regrettable drawback - it only works for the console session on a Terminal Services-enabled system. The reason is that each session has its own copy of WIN32K's global data memory, including the ".data" section which houses _W32pServiceTable - perhaps an afterthought intended to makes Terminal Services possible with a WIN32K.SYS inextricably bound to its use of global variables. (Thanks to eEye engineer Robert Ross for pointing this out!) Perhaps a future version of SysRq could overcome this by hooking the NtUserRegisterClassExWOW code directly, a change that would likely persist across all terminal sessions.
ConclusionIn this article, we've discussed the technical details of how we implemented SysRq, an eEye BootRoot-based utility that provides a SYSTEM shell on demand. It builds upon our previous work on reverse engineering how Windows hotkeys operate, in order to register a hotkey within the Winlogon system process for the purpose of opening the shell on any desktop. We hope that we've succeeded in presenting some novel and interesting techniques for navigating the kernel from non-image code, in the context of a unique and practical project.
For the record, credit for the SysRq concept goes to eEye's Ben Nagy, although he suggested that the interface to the backdoor be a pi symbol in a corner of the screen. That feature will likely be included when we port SysRq to Gibson OS.
Source: Derek Soeder, Software EngineerAsk ResearchQ: What is a buffer overflow?
A: Books have been written solely on this subject, and in the space allowed here we won't necessarily be able do it any justice. In a nutshell, a buffer overflow is simply a condition where more data is written to an allocated space of memory (
buffer) and thus the excess spills over (or
overflows) into other areas of memory. There are two types of buffer overflows, differing in the type of memory allocated and the information that is typically overwritten. In the case of a
stack overflow, the excess data typically overwrites the function’s saved return address. By manipulating this address it is possible to change the flow of execution upon returning from the function. The other type is a
heap overflow, which generally overwrites a function pointer which similarly disrupts the intended flow of operations. In the context of security, this is an opportunity for malicious users to manipulate these blocks by inserting and executing their own code.
We have reviewed our target audience for VICE and have seen an opportunity to attract more readers. Much of the featured articles, thus far, have required a certain requisite level of knowledge as the authors have assumed that the reader's already have a fundamental understanding of concepts such as reverse engineering. and are comfortable using common researcher tools such as IDA or SoftICE. In an upcoming issue of VICE, we will be opening our Newb Corner which will assume nothing and will attempt to clearly define a featured concept. You can expect to find this topic, buffer overflows, featured in one of the first of the Newb Corner series.
Q: What are some preventive measures one can take to reduce, if not eliminate buffer overflows?
A: Again, this could be, and certainly has been, discussed at length. Preventive measures must certainly come from the programmer of the application.
Lower-level languages such as C/C++ and Assembly, which allow the programmer to directly manipulate memory, present greater potential for buffer overflows than higher-level languages that tend to hide memory allocation and pointer arithmetic from the programmer. Also, the programmer must
enforce areas of input, such as a passed parameter or a web form textbox, with error handling to not allow for input greater than the allocated space. Explicit error handling is a must -- never assume the user will abide by the format of the requested information.
Integer overflows should also be considered. They are not so much a type of buffer overflow, but rather a condition that leads to an overflow when considering the strictest sense of the term. This can happen, for instance, when a variable declared is smaller than the result of an arithmetic operation. It is easy to imagine a series of power functions using modestly sized byte or double-byte values quickly taking an integer beyond even the largest of the declaration types.
Finally, there are
software code auditors that will parse source code using heuristics and flag areas of concern. The last line of defense is at the time of compilation, and there are
compiler switches that can be passed such as the /GS flag introduced in Microsoft's .NET 2003 ide. We have been informed that there are already publicly disclosed instructions on how to defeat this; however, it still offers some rudimentary protection. Above all, there is certainly no substitute for a well-trained programmer versed in security concepts; in most cases, [directed to the programmers] buffer overflows are your fault.
On the other hand, sometimes you aren't the programmer of the application and you need protection from such buffer overflows as a user of the software. Non-intrusive, non-signature based intrusion prevention systems, preferably host-based solutions, should be considered. eEye's
Blink IPS solution provides generic buffer overflow protection, in addition to the protection offered below the application layer, which is completely non-intrusive.
Have a question you would like answered? Send it to vice@eeye.com, and win an eEye t-shirt if we select your question for an upcoming newsletter.EtceteraMicrosoft Research Releases a Prototype OSMicrosoft Research has published the first details of a wholly new operating system under development called Singularity, designed new from the ground up, built on a new language and designed with emphasis on dependability instead of performance.
How to SubscribeTo subscribe to this and other eEye newsletters, please visit:
http://www.eeye.com/html/resources/newsletters/subscribe.htmlFeedbackThe eEye newsletter staff welcomes any comments, questions or suggestions from our readers. We hope that you will not hesitate to contact us with any feedback you may have. Send all feedback to
vice@eeye.com.
DisclaimerThe information within this newsletter may change without notice. Use of this information constitutes acceptance for use in an AS IS condition. There are NO warranties with regard to this information. In no event shall the author be liable for any damages whatsoever arising out of or in connection with the use or spread of this information. Any use of this information is at the user's own risk. Opinions expressed are not necessarily those of eEye Digital Security.
NoticePermission is hereby granted for the redistribution of this newsletter electronically. It is not to be edited in any way without the express consent of eEye. If you wish to reprint the whole or any part of this newsletter in any other medium excluding electronic medium, please email
vice@eeye.com for permission. Permission to reprint readers' comments or questions, and edit where necessary for length and clarity, is assumed unless explicitly forbidden.