Today, I would like to present a detailed description of the CVE-2011-1281 vulnerability [1], which was reported by me several months ago and patched today, together with four other bugs marked as the Elevation of Privileges class, on the occasion of the monthly Microsoft Patch Tuesday cycle (see Microsoft Security Bulletin MS11-056, a summary of the flaws’ origin and severity). All of the issues were present in the Windows CSRSS (Client/Server Runtime Subsystem) component, already mentioned in several of my posts [2][3][4] and articles [5][6]. Some of these problems affected every edition of the Windows NT-family systems up to Windows 2008/7, while the remaining part was only present up to Windows Vista. The latter is primarily caused by the fact, that all of the flaws were found in the Console Management code present in winsrv.dll (one of the modules used by the Windows Subsystem). Due to some major architecture changes applied in Windows 7 [7], the console support was (almost) entirely moved from the privileged CSRSS process into CONHOST.EXE, running in the context of the local user.

The blog post is meant to open up a series of technical write ups, explaining the origin and exploitation process of all the CSRSS issues just fixed. Apart from five high-impact vulnerabilities, publically announced by Microsoft, I will also present two Denial of Service bugs, which can be used to generate an unhandled Access Violation exception, resulting in the CSRSS crash and a Blue Screen of Death. A complete list of the flaws to be discussed, together with their root cause is shown below:

CVE-2011-1281 CSRSS Local EOP AllocConsole Vulnerability Lack of Sanity Check CVE-2011-1282 CSRSS Local EOP SrvSetConsoleLocalEUDC Vulnerability Integer Signedness Error CVE-2011-1283 CSRSS Local EOP SrvSetConsoleNumberOfCommand Vulnerability Integer Signedness Error CVE-2011-1284 CSRSS Local EOP SrvWriteConsoleOutput Vulnerability Code Logic Error CVE-2011-1870 CSRSS Local EOP SrvWriteConsoleOutputString Vulnerability Integer Overflow DoS Vulnerability #1 – Invalid 16-bit Integer Wrap DoS Vulnerability #2 – Integer Signedness Error

Along with the operating system internals and technical information related to the first vulnerability from the list, I am also going to cover possible exploitation vectors, which can be used to achieve reliable code execution in a real environment. The vulnerability itself is an example of a Handle-based Use-after-free condition, and is – to my best knowledge – the first publicly disclosed and documented vulnerability of this type. In order to better understand the techniques and concepts presented herein, you are strongly adviced to read the Windows Numeric Handle Allocation in Depth [8] article, providing detailed information about the handle allocation mechanisms found in the Windows NT kernel. Have fun, and stay tuned for successive blog entries! As always, comments of any kind are encouraged :-)

Note: Despite newer Windows platforms being affected as well, all exploitation considerations contained in this article are only confirmed for the Windows XP operating system. Furthermore, CSRSS not being part of the Windows kernel, its sources cannot be found in the WRK (Windows Research Kernel) package. Consequently, as the C code listings presented herein have a strictly illustrative purpose, some of them come form the ReactOS project source code (explicitly marked).

Before proceeding to technical details, you can watch an exploitation video:

The basics



One of the most elementary assumptions of the Windows console support, is the fact that a single process can be assigned a maximum of one console. The statement can be found in numerous locations thorough the MSDN documentation, e.g. the AllocConsole and AttachConsole function references:

A process can be associated with only one console, so the AllocConsole function fails if the calling process already has a console.

A process can be attached to at most one console. If the calling process is already attached to a console, the error code returned is ERROR_ACCESS_DENIED (5).

If we take a look at the kernel32.dll implementation of the AllocConsole routine, it turns out that the developers did remember about the aforementioned principle:

.text:7C87235E mov eax, large fs:18h .text:7C872364 mov [ebp+var_43C], eax .text:7C87236A mov eax, [eax+30h] .text:7C87236D mov eax, [eax+10h] .text:7C872370 cmp [eax+10h], ebx .text:7C872373 jz short loc_7C872387 .text:7C872375 push 5 ; dwErrCode .text:7C872377 call _SetLastError@4 ; SetLastError(x) .text:7C87237C mov [ebp+var_42C], ebx

The assembly code can be easily translated into the following C snippet:

if(CurrentProcessPEB()->ProcessParameters->ConsoleHandle != NULL) { SetLastError(ERROR_ACCESS_DENIED); return (FALSE); }

Apparently, the condition is correctly verified on the application’s (client) side. Following that code, numerous calls to internal kernel32 routines are issued, including AllocConsoleInternal – a function primarily responsible for sending the actual console creation request to the Windows Subsystem. The goal is achieved by packing the configuration data into a special shared buffer, and calling ntdll!CsrClientCallServer with the SrvAllocConsole operation code. Until now, every single part of the execution path – such as avoiding input sanitization or modifying functions’ parameters – could have been modified by the application itself.

.text:7C871F2B push 2Ch .text:7C871F2D push 20224h .text:7C871F32 push ebx .text:7C871F33 lea eax, [ebp+var_BC] .text:7C871F39 push eax .text:7C871F3A call ds:__imp__CsrClientCallServer@16 ; CsrClientCallServer(x,x,x,x) .text:7C871F40 xor esi, esi .text:7C871F42 cmp [ebp+var_9C], esi

After the console request is sent and dispatched by CSRSS, there is not much of a control, anymore. An (A)LPC message is first received by the csrsrv!CsrApiRequestThread function, and then passed to an adequate operation handler – that is, winsrv!SrvAllocConsole. Its prologue starts with the following assembly lines:

.text:75B4D1A7 ; int __stdcall SrvAllocConsole(LPHANDLE lpTargetHandle, int) .text:75B4D1A7 _SrvAllocConsole@8 proc near ; DATA XREF: .text:75B38A80 o .text:75B4D1A7 .text:75B4D1A7 var_C = byte ptr -0Ch .text:75B4D1A7 var_4 = dword ptr -4 .text:75B4D1A7 lpTargetHandle = dword ptr 8 .text:75B4D1A7 .text:75B4D1A7 mov edi, edi .text:75B4D1A9 push ebp .text:75B4D1AA mov ebp, esp .text:75B4D1AC sub esp, 0Ch .text:75B4D1AF push ebx .text:75B4D1B0 mov ebx, [ebp+lpTargetHandle] .text:75B4D1B3 push esi .text:75B4D1B4 push edi .text:75B4D1B5 lea esi, [ebx+28h] .text:75B4D1B8 mov eax, large fs:18h .text:75B4D1BE mov eax, [eax+3Ch] .text:75B4D1C1 mov eax, [eax+20h] .text:75B4D1C4 mov eax, [eax+74h] .text:75B4D1C7 mov edi, ds:__imp__CsrValidateMessageBuffer@16 .text:75B4D1CD push 1 .text:75B4D1CF push dword ptr [esi+4]

As can be seen, the routine begins from validating the input buffers, and than proceeds (not shown) straight to console allocation. (Un)surprisingly, the code doesn’t check, whether there is a text interface already associated with the client process! The above observation leads to a trivial conclusion – the only conditional jump preventing an application from creating more than one console at a time, is performed in the local context of the requestor itself!

The following function (gcc-compatible) can be used to zero out the “ConsoleHandle” field, so that multiple kernel32!AllocConsole calls do not fail anymore:

VOID ClearHandle() { // fs:[0x18] points to the virtual address // of the Thread Environment Block (TEB) structure. __asm("mov eax, fs:[0x18]"); // kd> dt _TEB // nt!_TEB // +0x000 NtTib : _NT_TIB // +0x01c EnvironmentPointer : Ptr32 Void // +0x020 ClientId : _CLIENT_ID // +0x028 ActiveRpcHandle : Ptr32 Void // +0x02c ThreadLocalStoragePointer : Ptr32 Void // +0x030 ProcessEnvironmentBlock : Ptr32 _PEB __asm("mov eax, [eax+0x30]"); // kd> dt _PEB // nt!_PEB // +0x000 InheritedAddressSpace : UChar // +0x001 ReadImageFileExecOptions : UChar // +0x002 BeingDebugged : UChar // +0x003 SpareBool : UChar // +0x004 Mutant : Ptr32 Void // +0x008 ImageBaseAddress : Ptr32 Void // +0x00c Ldr : Ptr32 _PEB_LDR_DATA // +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS __asm("mov eax, [eax+0x10]"); // kd> dt _RTL_USER_PROCESS_PARAMETERS // nt!_RTL_USER_PROCESS_PARAMETERS // +0x000 MaximumLength : Uint4B // +0x004 Length : Uint4B // +0x008 Flags : Uint4B // +0x00c DebugFlags : Uint4B // +0x010 ConsoleHandle : Ptr32 Void __asm("mov dword ptr [eax+0x10], 0"); }

Given the above procedure, one may successfully execute the following code:

AllocConsole(); // (1) ClearConsole(); AllocConsole(); // (2)

consequently getting CSRSS to create two console windows. Even though the first one is still displayed on the screen and has its events dispatched, it is now a zombie console. Namely, it becomes impossible to reference such a window by any means, unless it has another process attached, which we assume it does not. What is even more interesting, the window remains present on the user’s desktop, even after the parent application terminates. Given the circumstances, the flaw already enables a potential attacker to consume the machine resources (physical memory), in such a way it cannot be released until system reboot – in other words, a typical Denial of Service condition.

The result of running the following code snippet:

while(1) { AllocConsole(); ClearHandle(); }

is shown below:

Going deeper

Having just achieved a reliable DoS condition, it is time to wonder if the vulnerability can be used to do anything more than that. Fortunately, it can. Being designed to manage a maximum of one console per process, CSRSS is unable to store more information that the developers originally assumed, when creating internal structure definitions. Consequently, the per-process structure held by the Windows Subsystem only contains a single field to store the console handle. The above can be confirmed by investigating the ReactOS code (an accurate copy of the original Windows sources):

typedef struct _CSRSS_PROCESS_DATA { struct tagCSRSS_CONSOLE *Console; struct tagCSRSS_CONSOLE *ParentConsole; (...) HANDLE ConsoleEvent; (...) } CSRSS_PROCESS_DATA, *PCSRSS_PROCESS_DATA;

As a direct result, it is not possible to allocate multiple consoles in the context of one process, without dealing damage to the structures’ references. Thus, associating a second text window to a process that already owns one console gets CSRSS to overwrite the information about previous allocations:

/* Set the Process Console */ ProcessData->Console = Console;

The worst thing about the behavior is that the previous console is not freed by the faulty SrvAllocConsole code – it is simply forgotten about, as if it never existed – hence, the text window’s reference count is not decremented. Normally, CSRSS decrements the console refcount upon the termination of one of the console clients; however, the operation is only performed in the context of one, single console currently assigned to the terminating application (referenced through ProcessData->Console).

This leaves us with one or more dangling consoles, which in themselves do not pose a serious problem (they only occupy the virtual space of the subsystem process). What should be noted, however, is the fact that such console objects still contain client-related information (i.e. the parent process handle), which might have turned invalid since the console allocation time. Therefore, we might try to achieve interesting results, if we were able to make CSRSS utilize this outdated information in some way. Let’s take a look at some options.

In order to implement parts of the console functionality, the CSRSS process sometimes calls back to the owner (or clients) of the console window. More precisely, callbacks are used in two specific situations:

A Control Event is generated in the context of the console. It can be accomplished both programatically – by using the GenerateConsoleCtrlEvent API – or manually – by issuing the CTRL+C or CTRL+Break hotkeys.

The user wants to set the window properties, by choosing the “Properties” / “Defaults” option from the console context menu.

The first item is a well documented functionality, as the application itself is able to register its own callback routines called upon a control event occurence (see SetConsoleCtrlHandler), while the other one is an internal solution, not referenced in any of the official documents. In order to issue a callback, the subsystem uses the information present in the console descriptor – most notably, the client process handle. The entire mechanism has been thoroughly explained in the Windows CSRSS Tips & Tricks [6] article; for us, there are two essential observations to make:

The CSRSS → Client callbacks are triggered by having CSRSS create a new thread in the context of the client process: .text:75B48504 ; int __stdcall InternalCreateCallbackThread(HANDLE hProcess, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter) .text:75B48504 _InternalCreateCallbackThread@12 proc near ; CODE XREF: WowExitTask(x)+40 p .text:75B48504 ; CreateCtrlThread(x,x,x,x,x)+10E p ... .text:75B48504 (...) .text:75B485A5 lea eax, [ebp+ThreadId] .text:75B485A8 push eax ; lpThreadId .text:75B485A9 push ebx ; dwCreationFlags .text:75B485AA push [ebp+lpParameter] ; lpParameter .text:75B485AD lea eax, [ebp+ThreadAttributes] .text:75B485B0 push [ebp+lpStartAddress] ; lpStartAddress .text:75B485B3 push ebx ; dwStackSize .text:75B485B4 push eax ; lpThreadAttributes .text:75B485B5 push [ebp+hProcess] ; hProcess .text:75B485B8 call ds:__imp__CreateRemoteThread@28 ; CreateRemoteThread(x,x,x,x,x,x,x) (...) The “lpStartAddress” parameter is fully controlled by the parent process of the console.

Investigating the InternalCreateCallbackThread routine x-refs leads to the following function call chains:

winsrv!ProcessCtrlEvents → winsrv!CreateCtrlThread → winsrv!InternalCreateCallbackThread

winsrv!InitWindowClass → winsrv!ConsoleWindowProc → winsrv!PropertiesDlgShow → winsrv!InternalCreateCallbackThread

Both execution paths can be easily triggered by the attacker’s application (either by calling GenerateConsoleCtrlEvent, or SendMessage), thus an attacker is able to get CSRSS to call CreateRemoteThread with a freed process handle and a controlled start address! The described behavior can be confirmed by creating a dangling console, terminating the parent process, choosing the “Properties” option, and watching the CSRSS API calls:

kd> U winsrv!InternalCreateCallbackThread+0x17: 001b:75b7851b ff151c13b675 call dword ptr [winsrv!_imp__NtOpenProcessToken (75b6131c)] 001b:75b78521 85c0 test eax,eax 001b:75b78523 7d07 jge winsrv!InternalCreateCallbackThread+0x28 (75b7852c) 001b:75b78525 33c0 xor eax,eax 001b:75b78527 e9c4000000 jmp winsrv!InternalCreateCallbackThread+0xec (75b785f0) 001b:75b7852c 56 push esi 001b:75b7852d 8b350813b675 mov esi,dword ptr [winsrv!_imp__NtQueryInformationToken (75b61308)] 001b:75b78533 57 push edi kd> Dd @esp 010bfba8 0000051c 00000008 010bfbe8 01ebfa78 010bfbb8 010bfbd4 7e428dd9 00010564 000000a4 010bfbc8 010bfedc 009475b8 010bfbec 7e418b26 010bfbd8 009475b8 010bfbf8 75b617f1 00000000 010bfbe8 01ebf6d8 00000000 010bfc24 75b8f412 010bfbf8 0000051c 7c872101 00010564 00000112 010bfc08 01ec01a0 0000fff8 7e44048f 010bfe64 010bfc18 7c872101 00010564 00000000 010bfe74 kd> !handle 51c processor number 0, process 821d2da0 PROCESS 821d2da0 SessionId: 0 Cid: 025c Peb: 7ffd8000 ParentCid: 021c DirBase: 09308040 ObjectTable: e15394b8 HandleCount: 2774. Image: csrss.exe Handle table at e1222000 with 2774 Entries in use 051c: free handle, Entry address e17fba38, Next Entry 0000030c

Given the above, we end up being able to perform critical operations on undefined HANDLE values, in the context of a SYSTEM process. That’s certainly good news; the only problem is – how the flawed behavior can be used to escalate the privileges of the local user… Let’s find out :-)

Exploitation: Stage One

At the current stage, we can get CSRSS to use a previously-freed handle, assuming it is a valid Process Object identifier. In order for the exploit to work, it is required to re-assign the handle to another, highly-privileged process. The task can be accomplished, if we are able to (indirectly) control the number, types and order of handle allocation and deallocation, performed by the subsystem process.

The exact free handle management algorithms employed by the Windows kernel has been described in [8]. To make a long story short, the operating system manages a simple LIFO (Last In, First Out) queue called the free-list, on behalf of CSRSS. Every time a new handle is requested by the process (through OpenProcess, OpenThread, or any other API /service), the first item from the queue is popped, and assigned to an object. Similarly, when the NtClose service is called from within ring-3, the freed handle value is placed at the beginning of the queue.

Due to the fact that no tools designed to investigate Windows handle free-lists could be found on the internet at the time of the research, I decided to develop a really basic utility on my own. The project is called Windows Handle Lister, and is available through the Google Code website [9]. An example output of the program is as follows:

-=*( Microsoft Windows XP SP3 Handle-Table Lister v0.0.1 by j00ru//vx )*=- Loading driver... Opening driver... Querying for the driver version... Driver Version: Microsoft Windows XP Handle-Table Lister v0.0.1 by Matt "j00ru" Jurczyk Enter the target PID (less than 65536), or the handle table address (greater than 65536): 592 Setting the current process identifier... ----- FREE LIST: 0x000005b8 ---> 0x00000378 ---> 0x0000016c ---> 0x00000324 ---> 0x000000cc ---> 0x00000220 ---> 0x000005f4 ---> 0x000005c0 ---> 0x000005fc ---> 0x000005f8 ---> 0x00000584 ---> 0x000002c8 ---> 0x000004ac ---> 0x00000600 ---> 0x00000604 ---> (...) 0x000007e8 ---> 0x000007ec ---> 0x000007f0 ---> 0x000007f4 ---> 0x000007f8 ---> 0x000007fc ---> 0x00000000 ---> (null) ----- ALTERNATE FREE LIST: 0x00000000 ---> (null) Press enter to obtain a fresh copy of the handle free-lists. or Ctrl+C to quit.

When a new process is created in the system, a total of three handles are allocated in the context of CSRSS, in the following order and locations:

Process Object handle, allocated in basesrv!BaseSrvCreateProcess by NtDuplicateObject Thread Object handle, allocated in basesrv!BaseSrvCreateThread by NtOpenThread Port Object handle, allocated in csrsrv!CsrApiHandleConnectionRequest by NtAcceptConnectPort

Consequently, the first item present on the current free-list is always assigned to the newly-spawned process. On the other hand, when a simple (single-threaded) process is terminated, the handles are freed in the following order:

Thread Object handle Port Object handle Process Object handle

Although the above lists give us plenty of information required to reliably control the free-list, we must keep in mind that the internal state of CSRSS is highly dependant on the operating system state – every time a new process or thread is created or terminated, the contents of the handle queue change. What is more, (indirectly) spawning a process with the NT AUTHORITY\SYSTEM privileges might potentially require creating an unknown (but reasonably low) amount of other processes and threads. In general, it is almost impossible for an attacker to set up an ideal free-list, with the accuracy of one handle. In order to bypass the problem, I would like to propose a simple technique called Handle-spraying.

The basic concept of the solution is to fill the free-list with store large amounts of process handles (associated with consoles), instead of a single one. By spraying the queue with, say, 100 items, it doesn’t matter if the handle assigned to the privileged process is picked as the first, third, fifteenth or fifty second.

Let’s consider a queue made up of handles in the following pattern:

Free-list ⇒ 1 → 2 → 3 → 4 → 5 → … → 1000 → ∅



After creating 100 simple processes one after another, the lists will look like the following:

Process handles ⇒ 1 → 4 → 7 → 10 → … → 298

Thread handles ⇒ 2 → 5 → 8 → 11 → … → 299

Port handles ⇒ 3 → 6 → 9 → 12 → … → 300

Free-list ⇒ 301 → 302 → 303 → … → 1000 → ∅

When each of the created processes creates a single zombie console, we terminate all of them. Due to the thread / port handle swap, the queue will have the following form:

Free-list ⇒ 1 → 3 → 2 → 4 → 6 → … → 1000 → ∅



Additionally, each of the 100 former process handles (1, 4, …) is now associated with a dangling console, and will be used as the CreateRemoteThread argument, when triggered. Now, we can manipulate the specific layout of the free-list by creating and terminating threads in an appropriate order (CSRSS keeps tracks of all the processes / threads running on the system, thus it opens a handle to every execution unit). For example, we can create 300 threads:

Thread handles ⇒ 1 → 3 → 2 → 4 → 6 → … → 300

Free-list ⇒ 301 → 302 → 303 → … → 1000 → ∅

Next then, we shall free 200 handles – the ones previously associated with threads and ports:

Thread handles ⇒ 1 → 4 → 7 → 10 → … → 298

Free-list ⇒ 2 → 3 → 5 → 6 → 8 → 9 → … → 1000 → ∅

Finally, the remaining handles are deallocated:

Free-list ? 1 → 4 → 7 → 10 → 13 → 16 → … → 1000 → ∅

Thanks to the re-arrangments, we end up with a fully operational free-list, with one hundred of the initial items being handles associated with a console object. After following the above steps, we no longer have to worry if the privileged process (used in the exploitation process) will be assigned a formed Process Object identifier, or not – because it always will.

Exploitation – Stage Two

The second problem I encountered, while thinking of possible exploitation vectors, was the exact way of spawning a highly privileged program from within a restricted account. The main and mostly considered option was to initiate the creation of a service process (e.g. by using the Help and Support Center, or some other system functionality, which requires a certain service to work). After a short period of experiments, I found out that there is a much simpler solution. On Windows XP, using the win+u hotkey for the first time during the system session typically results in WINLOGON.EXE spawning several processes, one of which being UTILMAN.EXE with the Local System rights.

UTILMAN.EXE stands for “Utility Manager”, and is responsible for the Ease of Access options’ management. More specifically, it can be used to open up other helper applications, such as Magnifier, Narrator, or On-Screen Keyboard. The interesting thing about the manager is that it can be used, while the Winlogon screen (logon prompt) is active – hence the extraordinary privileges of the process. I believe that other, interesting methods of getting the OS to create a new, privileged process exist; however, the “utilman” one is perfectly valid in the context of a Proof of Concept – in order to achieve reliable exploitation in real conditions, “Stage Two” would probably have to be carried out in a different way.

Exploitation – Stage Three

The last exploitation stage which must be completed before seeing a shiny, brand new CMD.EXE with the NT AUTHORITY\SYSTEM security token, is moving the payload bytes into the memory context of UTILMAN. Obviously, it is not possible to use the OpenProcess + WriteProcessMemory API pair, due to the fact that processes running under a restricted user’s account are unable to open handles to programs with System-level rights. Trying to sneak some executable code through the environment variables is not a viable option, either; the security token difference makes it impossible to pass data through that communication channel. Not many options left…

One thing that the two processes (exploit and UTILMAN) have in common, is the desktop these two programs operate on. It turns out that WIN32K.SYS – the main graphical kernel module on Windows – manages two shared sections (a per-session and a per-desktop one), mapped in the context of every GUI process (a process becomes graphical after issuing a call to one of the WIN32K system calls). One of these sections contains the characteristics of windows present on the considered desktop, including arrays of data (e.g. unicode windows titles, editbox values and more). Consequently, a malicious application is able to store arbitrary bytes in the memory context of a highly-privileged process in the system, just by manipulating or creating basic windows on the local desktop.

In order to address ASLR (Address Space Layout Randomization), it is even possible to perform a simple form of memory spraying, by creating multiple windows with overlong titles. This way, we can set the remote thread’s StartAddress to a constant value, and simply assume that the shared section mapping will be large enough to cover the chosen memory area. For the illustrative purpose of a PoC, 40 windows with a 32kB title each, proved to guarantee almost 100% success rate on the test machine (the virtual address being hit was 0x00606060). Interestingly, the shared section mapping on Windows XP is not only readable, but also executable (see the section attributes on the above screenshot)! This fortunate coincidence settles any potential disputes about the DEP mechanism being able to disrupt the exploitation process. On the other hand, the “E” attribute has been removed from the mapping on Windows Vista, so the technique presented here would most likely cease to work on newer software configurations.

Exploitation – Final Notes

Having gone through all the stages needed to achieve a semi-reliable code execution with escalated privileges, let’s sum up the exploitation steps, needed to be taken from an exploit developer’s point of view:

Spray the shared WIN32K section, by creating a sufficient amount of USER objects. The section is then going to be mapped to every process running in the context of the local desktop, thus we can perform this step at this early point, Create N instances of a process, each of which will create a single zombie console and then go idle, (*) Kill all N instanes of the processes, Create 3N local threads, (**) Kill 2N threads (in the order described in the “Second Stage” section), Kill the remaining N threads, Emulate the win+u key presses, resulting in a new instance of UTILMAN.EXE being created, Call SendMessage(HWND_BROADCAST,WM_SYSCOMMAND,0xFFF7,0), triggering the execution of CreateRemoteThread on each of the N freed handles.

* – by creating a zombie console, we also mean replacing the original PropertiesProc address (used in kernel32!AllocConsole) with a custom pointer, as described in [6].

** – the technique is very time-sensitive. If any handle is picked / stored on the free-list between steps 3 and 4, than steps 5 and 6 might not succeed in setting up the expected free-list handle layout.

Funny facts

One particurarly funny fact I have came across, is related to the AllocConsole service implementation present in the ReactOS project sources:

CSR_API(CsrAllocConsole) { PCSRSS_CONSOLE Console; NTSTATUS Status = STATUS_SUCCESS; (...) RtlEnterCriticalSection(&ProcessData->HandleTableLock); if (ProcessData->Console) { DPRINT1("Process already has a console

"); RtlLeaveCriticalSection(&ProcessData->HandleTableLock); return STATUS_INVALID_PARAMETER; }

As can be seen, the above code doesn’t lack the essential sanity check, which should be performed at the beginning of the winsrv!SrvAllocConsole handler, and therefore is not affected by the described vulnerability. Considering that a great part of the ReactOS implementation was build based on reverse engineering of the original Windows images, the author of the above snippet must have automatically assumed that the check must be performed. Either way, I find it quite amusing that the source code of a project – meant to be a reproduction of the Windows operating system – tends to be written in a better / more secure manner, than the original components being based on :-)

Conclusion

Personally, the discussed vulnerability is an interesting example, showing that the use-after-free vulnerability class is not only characteristic to web browsers, but can also be found in regular system software. Successful exploitation is made possible thanks to various details of the CSRSS component architecture, such as the restricted program’s ability to directly control and re-arrange the subsystem’s free-list, or the fact that CSRSS performs execution-critical tasks on the process handles (creating new threads in certain situations). Interestingly, having found the vulnerability three years ago, I left it as-is, believing that is just another random local DoS issue.

As previously mentioned, the presented exploitation techniques are only confirmed to be valid for Windows XP. In order to improve the EoP reliability, or port the exploitation to Windows Vista, the developer has to invent alternate ways of performing Stage Two and Stage Three, or namely:

Allocate a new CSRSS handle for a highly-privileged process (either by triggering NtOpenProcess, NtDuplicateObject or other service),

Fill (preferably: spray) this process’es memory areas with controlled bytes – the payload (preferably: inside pages with the Executable attribute)

Thank you for reading this exploitation write-up; more interesting articles are soon going to show up, describing both already fixed issues and possibly some zero-days (e.g. a reliable way of bypassing the Driver Signature Enforcement mechanism). Stay tuned, and don’t hesitate to leave your comments, especially including new concepts regarding one of the exploitation stages! :-)

References

[1] CVE-2011-1281. http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-1281

[2] Windows CSRSS Write Up: the basics (part 1/1). https://j00ru.vexillium.org/?p=492

[3] Windows CSRSS Write Up: Inter-process Communication (part 1/3). https://j00ru.vexillium.org/?p=502

[4] Windows CSRSS Write Up: Inter-process Communication (part 2/3). https://j00ru.vexillium.org/?p=527

[5] Custom console hosts on Windows 7 (Hack in the Box Magazine #4). http://magazine.hitb.org/issues/HITB-Ezine-Issue-004.pdf

[6] Windows CSRSS Tips & Tricks (Hack in the Box Magazine #5). http://magazine.hitb.org/issues/HITB-Ezine-Issue-005.pdf

[7] Windows 7 / Windows Server 2008 R2: Console Host. http://blogs.technet.com/b/askperf/archive/2009/10/05/windows-7-windows-server-2008-r2-console-host.aspx

[8] Windows Numeric Handle Allocation in Depth (Hack in the Box Magazine #6). http://magazine.hackinthebox.org/issues/HITB-Ezine-Issue-006.pdf

[9] Windows Handle Lister project @ Google Code, http://code.google.com/p/windows-handle-lister/