Window Messaging


Windows messaging is a bit confusing to people coming from other platforms, even earlier versions of Windows. The user interface (UI) is message driven; however, it's a poor choice for general-purpose IPC on modern Windows systems. This is in direct contrast to earlier versions of Windows, which used the message system to meet many IPC requirements. This change in approach is primarily because of the security issues associated with window messaging.

Windows provides two types of securable GUI objects: window stations (WindowStation) and desktops (Desktop). Their architecture and caveats for their use are covered in the following sections. However, note that this security model doesn't extend to the actual Window objects. This distinction is important to make, as it helps you grasp the implicit vulnerability in a privileged process being exposed to potentially malicious input in the form of window messages.

Window Stations Object

The window station is the primary method of isolating GUI-based communication. It contains essential GUI information, including a private atom table (a shared collection of strings), a clipboard, windows, and one or more desktop objects. Each logon session is associated with a single window station, along with every process on a Windows system. Processes can be moved between window stations, assuming the associated tokens have adequate privileges. Windows provides a single window station for keyboard, mouse, and the primary display: Winsta0. It's referred to as the "interactive window station." Windows Terminal Services creates an additional Winsta0 for each connected terminal session.

Each unique account associated with a running service has a separate window station, so all services running under the network service account share a single window station and desktop. Meanwhile, all services running under the local service account share a separate desktop and window station. The service window stations are named for the logon session identifier of the associated account. This means network services are on the Service-0x0-3e6$ window station, which corresponds to the hard-coded session identifier for the network service account. Meanwhile, local services are on the Service-0x0-3e5$ window station, which corresponds to the hard-coded session identifier for the local service account. Services that run in the context of other accounts are associated with similarly named window stations, although the session identifier is somewhat random.

The discretionary access control list (DACL) on a window station is quite strict; it limits window station access to essentially the system account and the owning user. For services, the DACL is assigned when the window station is created for the service account. For Winsta0, an access control entry (ACE) for the user's security ID (SID) is added to the DACL at logon and removed at logoff. One interesting twist occurs when a process is started in a context other than the window station's owner, such as through the RunAs service. In this case, the ACL of the window station isn't modified; instead, the process inherits an open handle to the window station from the parent process. Therefore, communication is allowed without violating security requirements.

The Desktop Object

A desktop object is a securable UI object that functions as a display surface for attached threads; every thread on the system is associated with a single desktop. Desktops exist as objects inside a window station, and a window station can contain any number of Desktops, although there are only two common configurations: Winsta0 and service window stations. Winsta0 contains three desktop objects: default (the interactive user desktop), Winlogon (the logon screen desktop), and the screen saver. Service window stations typically have only a default desktop.

The access control on a desktop determines which users can manipulate the display surface. Although it's important that attackers can't read a victim's screen arbitrarily, the standard DACL addresses this concern reasonably well. What a desktop doesn't handle is actually more interesting. That is, a desktop doesn't affect processing of window messages. A window is associated with a desktop at creation, but it's just a tag for display purposes. The actual messaging is handled via the window station, so you don't need to be very concerned with desktops in code auditing because they don't affect how input is processed.

Window Messages

Before you dig into the hazards of Windows messaging, you need some background on how everything works, especially if you've never programmed for Windows before. This section explains the basics of a windowed program. Readers already familiar with UI programming in Windows can choose to skip to the next section. UI windows receive events through the use of window messages that have the following structure:

typedef struct {     HWND hwnd;     UINT message;     WPARAM wParam;     LPARAM lParam;     DWORD time;     POINT pt; } MSG, *PMSG;


The message member indicates the type of event the target window is being informed of. The wParam and lParam values specify additional information about the message. The interpretation of these fields depends on the type of message. Finally, the time parameter indicates when the message was posted, and the pt variable indicates the coordinates of the mouse at the time the message was posted. Most message-handling routines are concerned only with the message, wParam, and lParam members, which are passed as separate parameters instead of being part of a single MSG structure parameter.

The OS delivers messages to windows in a first in, first out (FIFO) queue. These messages can be generated by system events, such as mouse movements or key presses. They can also be generated by other threads on the same desktop. Window messages control most aspects of the UI, including clipboard operations and the properties of a window.

These are the four essential steps in creating a functional windowed application:

1.

Creating a WindowProc() function to handle messages.

2.

Defining a class that associates this WindowProc() to a window type.

3.

Creating an instance of the Window class.

4.

Creating a message-processing loop

The first step in creating a window is to create the WindowProc() function, which handles all the messaging. The following code is a simple WindowProc() function that demonstrates the basic layout:

int MainWindowProc(HWND hWnd, UINT iMsg, WPARAM wParam,                    LPARAM lParam) {     switch(iMsg)     {     case WM_CREATE: // Initialize         return 0;     ... handle additional messages here ...     case WM_DESTROY: // Exit on WM_DESTROY         return PostQuitMessage( 0 );     default:         return DefWindowProc(hWnd,iMsg,wParam,lParam);     } }


As you can see, this function is primarily just a switch statement for handling window messages passed via the iMsg parameter. This example shows processing for the WM_CREATE and WM_QUIT messages, although it doesn't do much with them. The default message handler, DefWindowProc(), does most of the heavy lifting. It's the default case in the switch statement that handles all system messages and other messages not explicitly handled by the application, which make up the bulk of the message traffic.

Now that you understand a bit about the handler, you need to see how it's registered with the system. This registration is done with the RegisterClassEx() function, which associates a name with the handler in the context of a process. The following code is a simple function that registers the handler created in the previous example:

BOOL InitClass(HINSTANCE hInst) {     WNDCLASSEX wc; // Defines the class     ZeroMemory(&wc, sizeof(wnd));     wc.hInstance = hInst;     wc.lpszClassName = "Main";     wc.lpfnWndProc = ( WNDPROC ) MainWindowProc;     wc.cbSize = sizeof(WNDCLASSEX);     return RegisterClassEx( &wnd ); }


After the handler is registered, the final two steps are to create the window and start the window's message pump, as shown in the following code:

int APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmdLine,     int nCmdShow ) {     WINDOW hwnd;     InitClass(hInst);     // Create a message-only window     hwnd = CreateWindow( "Main", "Main", 0, 0, 0, 0, 0,         0, 0, HWND_MESSAGE, 0 );     // This is the message pump     while( GetMessage( &msg, 0, 0, 0 )         && GetMessage(&msg, (HWND) NULL, 0, 0) != -1)     {         TranslateMessage( &msg );         DispatchMessage( &msg );     }     return msg.wParam; }


This example shows the standard window message pump. The GetMessage() call simply blocks until it receives a message. It's followed by the translateMessage() call, which queues up and translates a series of virtual key signals (from keyboard input) and sends them as a single character string. Finally, the DispatchMessage() call forwards the message on to the appropriate WindowProc().

The code passes the HWND_MESSAGE parameter to CreateWindow(), which creates a message-only window. This type of window is never displayed; it just exists so that a process can receive and handle window messages. This window type was chosen for two reasons. First, it's the shortest one, which keeps you from being distracted with unnecessary details. Second, and more important, this type of window is used by services that accept window message input. You should be familiar with this window type because it's associated with the kinds of applications attackers target.

There's one final function to mention, which is SendMessage():

LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam,                    LPARAM lParam );


This function doesn't matter when you're reviewing code, but you need to be familiar with it to understand exploits associated with window messages. This function simply accepts a handle to a window, a message ID, and two parameters that are interpreted differently, depending on the message type. You've already seen the WM_CREATE and WM_QUIT messages, and the WM_TIMER and WM_PASTE messages are explained in the next section. Note that any process with a handle to a window station can send messages to any other window on a desktop object within that window station. All that's needed is a simple call to SendMessage().

Shatter Attacks

You might be wondering why the previous sections have gone through a whirlwind introduction to the Windows GUI. After all, the basic shatter attack was described in Chapter 2, "Design Review," so the concept should be clear. However, it's important to understand the extent of this issue. The Windows API ties a lot of functionality into a simple, unprotected, messaging architecture. Every aspect of the user interface is controlled by window messages, and the design of the API provides no method of restricting or verifying a message source. Of course, attackers must have access to a window station before they can send messages, but after they do, the potential for exploit can be fairly open ended.

The original shatter attack exploited window message design by sending a WM_PASTE message to a privileged process with a message pump on the same window station. The WM_PASTE message allows attackers to place a buffer of shell code in the address space of the privileged process. The attack is then completed by sending a WM_TIMER message that includes the address of the shell code buffer. The default handler for the WM_TIMER message simply accepts the address parameter as a function pointer, and then immediately runs the code that's pointed to. The result is a straightforward privilege escalation performed by running arbitrary code in the context of a privileged process.

The immediate response to the shatter vulnerability was to simply filter the WM_TIMER message in any privileged process interacting with a user's desktop. Unfortunately, the WM_TIMER message is just a symptom of the problem. The reality is that many messages allow manipulation of memory in a target process's address space or could lead to arbitrary execution. Brett Moore demonstrated a number of these messages in a speech at the Blackhat security conference (http://blackhat.com/presentations/bh-usa-04/bh-us-04-moore/bh-us-04-moorewhitepaper.pdf). However, there are certainly new exploitable messages that have yet to be considered. Plus, there are unique exploit vectors in each windowed process, which make it unreasonable to expect developers to anticipate every one. The root of the problem is that a privileged process, or specifically a service, can't safely interact with a potentially hostile desktop.

As a code auditor, you need to identify situations that cause a privileged service to interact with normal user desktops. This interaction can happen in two basic ways. The first is a simple operational concern; you just need to check the properties for a service and make sure the service isn't interactive. To do this, use the Services Microsoft Management Console (MMC) to open the Properties dialog box for the service. Then check the "Log On" tab to see whether the "Allow Service to Interact with Desktop" option is selected. If it is, the service is potentially vulnerable to a shatter attack. Figure 12-1 shows the Properties dialog box for the Windows Task Scheduler, which is an interactive service.

Figure 12-1. An interactive Windows service


Services can use another method to interact with a user desktop; they can manually create a thread and window on the user's desktop. The following code shows this process:

HWINSTA hWinsta; HDESK hDesk; hWinsta = OpenWindowStation("Winsta0", FALSE, MAXIMUM_ALLOWED); SetProcessWindowStation(hwinsta); hdesk = OpenDesktop("default", 0, FALSE, MAXIMUM_ALLOWED); SetThreadDesktop(hDesk);


For brevity's sake, the error checking has been left out, but this code is essentially how a service sets up a thread on a normal user's desktop. This code simply opens a handle to Winsta0 and then uses the returned handle to open the default desktop. The current thread is then switched to this desktop, and the thread can interact with the logged-on user's desktop. Of course, the thread isn't vulnerable until it starts processing messages. Fortunately, you know how to identify that because you walked through a message window setup earlier. However, don't discount the existence of a message window just because you can't see it. For instance, certain COM applications can create background message windows (as explained in "COM" later in this chapter), so you need to be aware of these possibilities.

To summarize, when you audit a service, you should perform the following steps to identify potential shatter-attack exposures:

1.

Check the MMC snap-in for the service to see whether it runs as the interactive user.

2.

Examine the code to determine whether it manually attaches to the interactive user's desktop.

3.

If either case is true, determine whether a message pump is in operation for receiving window messages. If a message pump is in operation, you can consider the application to be at risk.

DDE

Dynamic Data Exchange (DDE) is a legacy form of IPC that exchanges data by using a combination of window messages and shared memory. It's done in one of two ways. This first requires handling WM_DDE_* window messages with the PackDDElParam() and UnpackDDElParam() functions. The second method uses the DDE Management Library (DDEML) API, which includes a set of Dde* functions that wrap the window message handling. You can refer to the MSDN for more particulars on using DDE communications.

DDE was a common form of IPC in earlier versions of Windows, but it has been mostly superseded by more robust mechanisms. DDE has no real security impact when used to establish communication between processes with the same security context. However, it can be used to establish communication between different user contexts on a shared window station or even exchange data over a network by using file shares. Just to make it more confusing, DDE supports impersonation of clients in a DDE communication. What you need to keep in mind is that any use of DDE between security contexts represents a potential shatter vulnerability. This includes network DDE, which requires a privileged service on the desktop. So vulnerable uses of DDE include the same type of setup as the shatter attacks described previously.

Terminal Sessions

Windows Terminal Services (WTS) provides the capability for a single Windows system to host multiple interactive user sessions. Originally, this capability was available as a separate product in Windows NT Terminal Server. However, it was eventually incorporated into the base product line in all versions of Windows XP. Terminal Services is not fully functional in most Windows XP versions, but it is a necessary component of the Remote Assistance and Fast User Switching (FUS) capabilities.

The introduction of WTS required some additional framework for interacting with different connections; this requirement was addressed by the addition of terminal sessions and their associated WTS API functions. Terminal sessions place additional restrictions on the interaction between processes in different sessions. For example, each terminal session has a unique Winsta0 associated with it, and objects are distinguished between sessions by using the Global\ and Local\ namespace prefixes. This naming setup allows the standard API functions to still work as expected, while the WTS API can be used for WTS-specific manipulation.

Versions of WTS before the Vista release have an interesting quirk. They run all services in session 0, which is the first session the system creates. It also happens to be the same session used by the first interactively logged-on user. Running all services in session 0 unintentionally grants some extra privilege to the console user on a terminal server and the first user on an FUS-enabled system. The main impact is that a session 0 user can communicate with interactive services.

As mentioned, an interactive service represents a serious vulnerability that could allow attackers to run arbitrary code in the context of a privileged service account. Windows Vista addresses this vulnerability by eliminating interactive services entirely. It restricts session 0 to services only and makes it a completely noninteractive session. You should make note that any software specifically targeting Windows Vista won't be vulnerable to the general class of shatter vulnerabilities.




The Art of Software Security Assessment. Identifying and Preventing Software Vulnerabilities
The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities
ISBN: 0321444426
EAN: 2147483647
Year: 2004
Pages: 194

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net