You may have heard that built-in services in Windows Vista were specifically hardened by Microsoft engineers during its development process. You might be wondering what that really means, how it works and, if you are a developer, how to harden your own services the Vista way. Jean-Yves Poublan, a Principal Security Consultant at Microsoft, and I are publishing a series of posts on how to leverage Windows Vista new architecture to make your services more secure.
Today, we start with a significant change with Windows Vista and Longhorn Server: Win32 services are now isolated in Session 0. So, what does this mean for developers?
‘windows’ on ‘desktops’ in ‘window stations’ in ‘Terminal Services sessions’
Windows NT was designed to be a multi-user system through the use of sessions. The SDK refers to these as Terminal Services sessions which are not to be confused with logon sessions, as they are not the same thing. Terminal Services sessions are created and managed by the session manager (smss.exe) which is one of the first processes created when the system starts. Logon sessions and processes somehow live within a Terminal Services session. Note : The Terminal Services session ID for a given logon session may be obtained by calling the LsaGetLogonSessionData API after having enumerated the logon sessions (LsaEnumerateLogonSessions). One may also get the Terminal Services session ID for a process with the GetTokenInformation(TokenSessionId) API after having obtained the primary token for the process (OpenProcessToken).
Previous to Windows Vista and Longhorn Server, Win32 services and user applications for the console user (as well as winlogon.exe and the Win32 subsystem – csrss.exe) were all started within Terminal Services Session 0. A second Terminal Services session (Session 1) was created when a second user logged on (such as a user connecting through Terminal Services on Windows Server 2003, or a second user logging on Windows XP through Fast User Switching), and so on.
So user applications for the console user always shared Session 0 with system services. This is no longer the case with Windows Vista and Longhorn server. What is wrong with having system services and user applications live in the same session? Well, it mostly has to do with interactive services.
First let’s recall what desktops and window stations are. Both are securable kernel objects in the sense that they are protected by ACLs. You can think of Terminal Services sessions having window stations that in turn contain desktops. There is a special window station called Winsta0 which is the windows station that is connected to the display and input devices. Processes are attached to a window station which they are going to use (supposedly) to interact with the user. Threads within a process are themselves attached to a desktop (within the window station) on which they display windows and they get input from the user. Windows messages are confined within a desktop, and Winsta0 will typically have three desktops: the winlogon desktop, the interactive desktop, and the screen saver desktop. Winsta0 grants rights to SYSTEM and the logon SID, so only the system and the currently logged on user can access the console. When a user logs off, the logon SID is removed from Winsta0, and when a new user logs on, the new logon SID is added to Winsta0’s ACL.
Windows, on the other hand, are user objects that are not securable by ACLs. So threads that have gained access to a desktop can send messages to any window on that desktop. In the past, applications that ran with higher privileges on the desktop have been vulnerable to the infamous shatter attacks from other malicious applications.
Normally, Win32 services are not attached to Winsta0 (they don’t have the rights on Winsta0), but instead they get their own window stations. Those window stations are not connected to any hardware so if a service displays a window and waits for user input, it may well wait forever…
Interactive services – to be avoided if at all possible
Interactive services are services that are configured as such (flag SERVICE_INTERACTIVE_PROCESS for CreateService or ChangeServiceConfig APIs). When the SCM starts a process for an interactive service, it attaches the service to Winsta0 instead of the service window station. In order to do so, the service process must run as SYSTEM (since only SYSTEM - and currently logged on user - have rights on Winsta0). Service threads can then attach to the interactive desktop and interact with the user.
One can see two compounded problems here: interactive services are vulnerable to Windows messages attacks from malicious user applications, and those attacks can result in privilege elevation since interactive services run as SYSTEM with TCB privilege. So deploying an interactive service that is vulnerable could compromise the whole system.
There are other things wrong with interactive services. On Windows Server 2003 with Terminal Services, the user that is currently logged on at the console in Session 0 may not be the user that the service should interact with. On Windows XP with Fast User Switching, it is even worse. The currently active console user may not be Session 0, but Session 1 (or Session n). In that case, if the interactive service waits for user input, it may wait forever from Session 0 which is not active. It is said that interactive services with Fast User Switching just don’t work. Because of that, developing and deploying interactive services has been strongly discouraged. As a matter of fact, interactive services can be banned from the system by setting the NoInteractiveServices registry value to 1 in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Windows. In that case, interactive services will still be started by the SCM (Services Control Manager), but they won’t be attached to Winsta0 (the SCM does log a warning in the event log). This is valid for Windows Vista and Longhorn as well.
Isolation of services in Session 0 with Windows Vista and Longhorn Server
With Windows Vista and Longhorn Server, user applications for the first logged on console user are now started in Terminal Services Session 1, and services in Session 0 are isolated from user applications. The second logged on user gets Session 2 and so on. Services isolation in Session 0 helps protecting the system from malicious user applications. Imagine one has installed an interactive service that is vulnerable, that interactive service will not share the desktop with (potentially) malicious user applications anymore and as such will be less likely to be compromised and used as a vehicle to attack the system.
Services isolation in Session 0 affects all services that assume they are running in the same session as user applications, and not only services that configured as interactive services. For instance, a service that communicates with user applications by way of Windows messages will no longer work. Also, a service that synchronizes with user applications through synchronization objects (semaphores, mutexes, etc…) created in the session private name space will no longer work as well. The global name space should be used instead (object names prefixed with Global\).
Under Windows Vista and Longhorn, a service can still be configured to be an interactive service, but such configuration does not make a whole lot of sense, since the interactive service will be attached to the Winsta0 in Session 0, which does not have a physical console and user to interact with.
If you have a service that is designed to be an interactive service and as such interacts with the desktop, or a service that assumes it is running in the same session as user applications, it is time to change it. Windows SDK does give some ideas of how a service could interact with users, without having to be an interactive service. This includes communicating with a user process through some form of IPC (preferably secure) channel, or using Terminal Services APIs such WTSSendMessage. Determining which user (in which target session) your service should interact with is up to you but should not be overlooked.
For legacy interactive services that cannot be changed in the short term, Windows Vista provides a compatibility mechanism called Interactive Service Detection service (ui0detect.exe). UI0Detect monitors interactive services in Wintsta0 of Session 0 and when such service displays a modal dialog it notifies the user in the currently active console session. The user can choose to switch to Winsta0/interactive desktop of Session 0 to respond to the dialog, and then switch back to the user session. UI0Detect is a temporary measure designed to limit the effect of Session 0 service isolation for existing interactive services that cannot be changed. As a service developer you should not count on that mechanism.
What about MessageBox() and MessageBox(MB_SERVICE_NOTIFICATION)?
When calling the MessageBox() API - *without* the MS_SERVICE_NOTIFICATION flag - from a service that is not an interactive service, the thread waits forever. It is worth noting that if a non interactive service is programmed in managed code on the .Net Runtime, calling the System.Windows.Forms.MessageBox.Show() method will raise an exception (“System.InvalidOperationException: Showing a modal dialog box or form when the application is not running in UserInteractive mode is not a valid operation. Specify the ServiceNotification or DefaultDesktopOnly style to display a notification from a service application”) instead of hanging there waiting for user input. Also if the service is an interactive service, under Windows Vista, UI0Detect will handle the interaction for MessageBox() – without MB_SERVICE_NOTIFICATION.
The MB_SERVICE_NOTIFICATION flag causes the system to redirect the message box to the interactive desktop (or winlogon desktop if there is no user logged on) on WinSta0 where it is handled by csrss.exe (the Win32 subsystem).It was designed to allow services that technically are not interactive services (i.e. they are not configured as interactive services and thus are not attached to Winsta0) to display a modal dialog message box on the interactive desktop and get user input.
Under Windows Vista RTM, this does not work however, in the sense that instead of the message box being redirected to the interactive desktop of a session with a user on it, or taken care of by UI0Detect as one could hope, the functions returns IDOK immediately with no user interaction whatsoever. It behaves that way even for services that are configured as interactive services and whose interactions are normally handled by UI0Detect. If your service uses MessageBox(MB_SERVICE_NOTIFICATION) to ask for user approval for some operation, it may need to be modified.
The following two tables summarize MessageBox behaviors under Windows XP SP2 and Windows Vista RTM.
NoInteractiveServices = 0
NoInteractiveServices = 1
Window station (service is running as SYSTEM)
"Allow service to interact with desktop"= unchecked
"Allow service to interact with desktop"= checked
No display – waiting forever (1)
Display OK (directly by service process)
No display – waiting forever (1)
SCM warning in event log when starting the service
Display OK (through csrss.exe)
Display OK (through csrss.exe)
MessageBox behavior on Windows XP SP2(1) Managed code: runtime will raise an exception
Interactive Service Detection – user can switch to Session 0 to respond service
No display – returns 1 (IDOK)(2) immediately
No display – returns 1 (IDOK)(2) immediately
MessageBox behavior on Windows Vista RTM(1) Managed code: runtime will raise an exception(2) Even if message box has no such button