Mysterie

IT Security researcher, bug hunter, code breaker

CVE-2011-1281 Privilege escalation in CSRSS proof of concept

31 Jul 2011 » security

After one year without blogging (all my apologies), I’m back. A few days ago, I’ve seen the pwnie awards nominations list. There were lot’s of interesting and sophisticated bug exploitation. But one attracts my attention « Privilege escalation in CSRSS » discovered by Matthew ‘j00ru’ Jurczyk. If you want to understand this vulnerability and the way to exploit it, read this excellent post http://j00ru.vexillium.org/?p=893. And if you’re not familiar with CSRSS, I advice you to read this article or this one (in French).

So, for writing the PoC we have to follow these steps :

  • 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.
** – 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.

I won’t speak about first step immediately for differents reasons. Let’s start with step two « create a single zombie console ». For me it’s the hardest part. We have to code AllocConsole and AllocConsoleInternal (I only scope Windows XP version for the moment). With AllocConsoleInternal we can control the PropRoutine & CtrlRoutine of the console. For coding this function I start googling with « AllocConsoleInternal + PropRoutine + CtrlRoutine » and reach this function definition :

BOOL APIENTRY AllocConsoleInternal(
 IN LPWSTR lpTitle,
 IN DWORD dwTitleLength,
 IN LPWSTR lpDesktop,
 IN DWORD dwDesktopLength,
 IN LPWSTR lpCurDir,
 IN DWORD dwCurDirLength,
 IN LPWSTR lpAppName,
 IN DWORD dwAppNameLength,
 IN LPTHREAD_START_ROUTINE CtrlRoutine,
 IN LPTHREAD_START_ROUTINE PropRoutine,
 IN OUT PCONSOLE_INFO pConsoleInfo);

With some call to ntdll!CsrAllocateCaptureBuffer, ntdll!CsrCaptureMessageBuffer for desktop, title and current dir memory allocation. And then ntdll!CsrClientCallServer with allocConsole request we will reach winsrv!SrvAllocConsole and then spawn a console. For testing we lunch a broken console and kill his process and his parent process, after that we do a « right clic + proprieties/default » on the broken console and then we have a winsrv!InternalCreateCallbackThread executed with free handle! (the killed parent process handle precisely).

Steps 3, 4, 5, 6, 8 are quite easy. Step 7 (WIN+U emulation) too but SetKeyboardState and PostMessage doesn’t work, we have to use keybd_event (depreciated) or SendInput to invoke utilman.exe.

Therefore, with all these steps we are able to get CSRSS to call CreateRemoteThread with a system process handle and a controlled start address. Now, we need step one « Spray the shared WIN32K section of system process with USER object » and it’s done! For this we have to invoke ultiman (WIN+U) which spawn three new process :

-> ultiman.exe         [NT AUTHORITY\SYSTEM]
+-> ultiman.exe /start [USER]
+–> narrator.exe /UM   [USER]

Then we create user object like MessageBox with over long title (32Ko). But ultiman (system) doesn’t share the win32k section with other process at all times. After trying different unsuccessful methods, I decided to contact j00ru who gave me the solution. We can inject user object in ultiman (system) if another user (regardless of his privileges) is logged on the machine at the same time. At this moment, I haven’t found explanation of this behavior. I think it’s something in relation with Desktop/Winstation/Session, if you have some ideas tell me.

https://www.youtube.com/watch?v=Rup-dDW4PN4

Source of the poc :
http://mysterie.fr/prog/blog/allocConsole/

Thanks to j00ru for his help and all shared knowledges on his blog, HITB and so on!