Session 0 Isolation - Native

Hands-On Lab

Session 0 Isolation - Native

Lab version:1.0.0

Last updated:10/6/2018

Contents

Overview

Exercise 1: Mitigating Service UI

Task 1 - Install and Run the Service

Task 2 - Modify the Service to Use WTSSendMessage (Quick-Fix)

Task 3 - Launch UI with Different User Credentials

Exercise 2: Securing Shared Objects

Task 1 - Install and Run the Service

Task 2 - Modify the Service to Create the Object in the Global Namespace

Task 3 - Modify the Service to Provide Security Attributes (DACL and SACL) for the Object

Exercise 3: Securing a File Object

Task 1 - Install and Run the Service

Task 2 - Modify the Integrity Level of the Log File

Summary

Overview

Services are an integral mechanism built into Microsoft Windows® operating systems. Services are different from user applications because you can configure them to run from the time a system starts up until it shuts down, without requiring an active user to be present. Services on Windows are responsible for all kinds of background activity that do not involve the user, ranging from the Remote Procedure Call (RPC) service to the Network Location Awareness service.

Some services may attempt to display user interface dialog boxes or communicate with user applications. Such services face compatibility problems with Windows7. Without taking the necessary precautions for properly securing the communication channel with user applications, your services will fail to work properly on Windows7.

Objectives

In thislab, you will learn how to:

  • Redesign and fix a service that attempts to display UI
  • Set appropriate security and access levels on kernel objects shared by services and applications

System Requirements

You must have the following items to complete this lab:

  • Microsoft Visual Studio®2008
  • Windows7
  • Windows Sysinternals Process Explorer

Exercise 1: Mitigating Service UI

In this exercise, you will install and run a service that attempts to display UI directly to the user. You will see the automatic mitigation (interactive services dialog detection) that is built-in to Windows and its effect on the user experience, and will modify the service so that it does not display UI directly.

You will also modify the service so that it launches its decoupled UI in a separate process running under the context of the currently active user.

Task 1 -Install and Run the Service

As part of this task, you will install the service using the sc command line utility and then run it for the first time. This service attempts to display a user interface dialog box that will trigger the service UI mitigation.

  1. Using Visual Studio, open the Session0_Starter solution.
  2. Build the entire solution (make note of the build configuration you used – Debug/Release, x86/x64).
  3. Open an administrative command prompt:
  4. Click Start.
  5. Point to All Programs.
  6. Point to Accessories.
  7. Right-click Command Prompt.
  8. Click Run as administrator.
  9. Use the cd command to navigate to the output directory that contains the application binaries. For example, if the output directory is C:\Session0_Starter\Debug, then use the following commands to navigate to that directory:

CMD

C:

cd C:\Session0_Starter\Debug

  1. Issue the following command to create the TimeService service

CMD

sc create TimeService binPath= C:\Session0_Starter\Debug\TimeService.exe

Help

Make sure to replace the path to the service with the path you used in Step 9, and make sure to copy the space after “binPath=”).

  1. Open the Services MMC Snap-in by clicking +R and typing services.mscinto the Run dialog box.
  2. Locate the TimeService service, right-click it, and click Start.

  1. After a few seconds, you will see a dialog box similar to the following image.

  1. This is the Interactive services dialog detection dialog box, which detects a service attempting to display UI and presents this mitigation fix.
  2. Click Remind me in a few minutes to dismiss the message or click Show me the message to switch to the secure Session 0 desktop and see the service UI (a message box).
  3. Stop the service by going back to the Services MMC Snap-in, locating the TimeService service, right-clicking it, and clicking Stop.

Task 2-Modify the Service to Use WTSSendMessage (Quick-Fix)

As part of this task, you will use the WTSSendMessage function to display a message box to the user. This will serve as a quick fix and replacement for displaying the Interactive services dialog detection dialog box. to the user.

  1. If you haven’t done so yet, follow steps 1-5 in Task 1 to install the TimeService service.
  2. If you haven’t done so yet after completing Task 1, make sure to stop the TimeService service (see step 10 in Task 1).
  3. Using Visual Studio, open the Session0_Starter solution.
  4. Locate the TimeService project under the UI\Native solution folder and open the TimeService.cpp file.
  5. Find the first //TODO comment in the file. Comment out the MessageBox function call and replace it with the following:

C++

LPWSTR lpszTitle = L"Time Change";

LPWSTR lpszText = L"Notification: 5 seconds have elapsed.\r\nWould you like to see more details?";

DWORD dwSession = WTSGetActiveConsoleSessionId();

WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, dwSession, lpszTitle,

static_cast<DWORD>((wcslen(lpszTitle) + 1) * sizeof(wchar_t)),

lpszText, static_cast<DWORD>((wcslen(lpszText) + 1) * sizeof(wchar_t)),

MB_YESNO|MB_ICONINFORMATION, 0 /*wait indefinitely*/, &dwResponse, TRUE);

  1. Build the solution.
  2. Repeat steps 6-7 from Task 1. You should see a message box appear on your main desktop asking you a question, without the Interactive service dialog detection dialog box standing in your way.
  1. Click No to dismiss the message.
  2. Stop the service (see step 10 from Task 1).

Task 3 - Launch UI with Different User Credentials

As part of this task, you will modify the service so that it launches a new interactive UI process running under the context of the currently active user, which will display the user interface on behalf of the service.

  1. Repeat steps 1-4 from Task 2.
  2. Find the second //TODO comment in the TimeService.cpp file.
  3. Begin with retrieving the active session ID and its related user token (see for background on user tokens) using the WTSGetActiveConsoleSessionId and WTSQueryUserToken functions. This is the token that will be used for creating the interactive UI process. Insert the following code:

C++

BOOL bSuccess = FALSE;

STARTUPINFO si = {0};

PROCESS_INFORMATION pi = {0};

si.cb = sizeof(si);

DWORD dwSessionID = WTSGetActiveConsoleSessionId();

HANDLE hToken = NULL;

if (WTSQueryUserToken(dwSessionID, &hToken) == FALSE)

{

goto Cleanup;

}

  1. Duplicate the token so that it can be used to create a process, using the DuplicateTokenEx function. Insert the following code:

C++

HANDLE hDuplicatedToken = NULL;

if (DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hDuplicatedToken) == FALSE)

{

goto Cleanup;

}

  1. Create an environment block for the interactive process, using the CreateEnvironmentBlock function. Insert the following code:

C++

LPVOID lpEnvironment = NULL;

if (CreateEnvironmentBlock(&lpEnvironment, hDuplicatedToken, FALSE) == FALSE)

{

goto Cleanup;

}

  1. Retrieve the full path of the client application by retrieving the full path to the service executable (using GetModuleFileName), stripping away the file name (using PathRemoveFileSpec), and then concatenating the client application name. Insert the following code:

C++

WCHAR lpszClientPath[MAX_PATH];

if (GetModuleFileName(NULL, lpszClientPath, MAX_PATH) == 0)

{

goto Cleanup;

}

PathRemoveFileSpec(lpszClientPath);

wcscat_s(lpszClientPath, sizeof(lpszClientPath)/sizeof(WCHAR), L"\\TimeServiceClient.exe");

  1. Create the process under the target user’s context using the CreateProcessAsUser function. Insert the following code:

C++

if (CreateProcessAsUser(hDuplicatedToken, lpszClientPath, NULL, NULL, NULL, FALSE,

NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,

lpEnvironment, NULL, &si, &pi) == FALSE)

{

goto Cleanup;

}

CloseHandle(pi.hProcess);

CloseHandle(pi.hThread);

bSuccess = TRUE;

  1. Make sure you have code in place to free resources allocated during this work. Insert the following code:

C++

Cleanup:

if (!bSuccess)

{

ShowMessage(L"An error occurred while creating fancy client UI", L"Error");

}

if (hToken != NULL)

CloseHandle(hToken);

if (hDuplicatedToken != NULL)

CloseHandle(hDuplicatedToken);

if (lpEnvironment != NULL)

DestroyEnvironmentBlock(lpEnvironment);

  1. Build the solution.
  2. Repeat steps 6-7 from Task 1. Without the Interactive service dialog detection dialog box standing in your way, you should see a message box appear on your main desktop asking you a question. Click Yes and a client application will be launched, presenting you with the current time.
  3. Close the client application and stop the service (see step 10 from Task 1).

Watch out

For purposes of this exercise, we simplified this sample code and did not adhere to all security-coding guidelines when we designed and implemented this project. Carefully consider possible security issues before creating a process under the context of another user and using that process to communicate back to the service.

Exercise 2: Securing Shared Objects

In this exercise, you will install and run a service that creates a kernel object (event) that is shared with a standard application. You will see that the event is not accessible to the standard application because it does not reside in the same session namespace, and because its access control rights are not configured properly.

Task 1 -Install and Run the Service

As part of this task, you will install the service using the sc command line utility and then run it for the first time. You will see that the service client receives an “Access Denied” error when it attempts to use the event created by the service.

  1. Enable User Account Control (UAC). From Start, click Search and enter “User Account Control.” Choose “Change User Account Control settings” from the search results. Then ensure that the slider is not set to Nevernotify.

  1. Using Visual Studio, open the Session0_Starter solution.
  2. Build the entire solution (make note of the build configuration you used – Debug/Release, x86/x64).
  3. To open an administrative command prompt, click Start, point to All Programs, point to Accessories, and then right-click Command Prompt. Click Run as administrator.
  4. Use the cd command to navigate to the output directory that contains the application binaries. For example, if the output directory is C:\Session0_Starter\Debug, then use the following commands to navigate to that directory:

CMD

C:
cd C:\Session0_Starter\Debug

  1. Issue the following command to create the AlertService service
    Note: Make sure to replace the path to the service with the path you used in step 4, and make sure to copy the space after “binPath=”).

CMD

sc create AlertService binPath= C:\Session0_Starter\Debug\AlertService.exe

  1. Open the Services MMC Snap-in by clicking +R and typing services.mscinto the Run dialog box.
  2. Locate the AlertService service, right-click it, and click Start.
  3. Open a standard command prompt. From Start, point to All Programs, click Accessories, and then click Command Prompt (Note: do not run the command prompt as an administrator).
  4. Repeat step 5 within the standard command prompt.
  5. Issue the following command to launch the AlertService client application, which attempts to open the event created by the service and use it for synchronization (WaitForSingleObject).

CMD

AlertServiceClient

  1. Note that the client fails to open the event with an error 2, meaning that the event could not be found.
  2. Stop the service by going back to the Services MMC Snap-in, locating the AlertService service, right-clicking it, and clicking Stop.

Task 2-Modify the Service to Create the Object in the Global Namespace

As part of this task, you will change the name of the object to include the prefix of the global namespace.

  1. If you haven’t done so yet, follow steps 1-5 in Task 1 to install the AlertService service.
  2. If you haven’t done so yet after completing Task 1, make sure to stop the AlertService service (see step 10 in Task 1).
  3. Using Visual Studio, open the Session0_Starter solution.
  4. Locate the AlertService project under the Security\Native solution folder, and open the AlertService.cpp file.
  5. In the file, find the //TODO comment marked with “STEP 1” and replace the call to CreateEvent with the following line:

C++

g_hAlertEvent = CreateEvent(NULL, FALSE, FALSE, L"Global\\AlertServiceEvent");

  1. Locate the AlertServiceClient project under the Security\Native solution folder, and open the AlertServiceClient.cpp file.
  2. In the file, find the //TODO comment marked with “STEP 1” and replace the call to OpenEvent with the following line:

C++

HANDLE hEvent = OpenEvent(SYNCHRONIZE, FALSE, L"Global\\AlertServiceEvent");

  1. Build the solution.
  2. Repeat steps 7-13 from Task 1. Note that the client still fails to access the event (this time because of security settings and not because of its namespace).
  3. Run Process Explorer from Windows Sysinternals (download the tools from if you do not have them).
  4. Select the AlertService service in the list of processes (if the service does not appear, from the File menu, click Show processes from all users to restart Process Explorer with administrative privileges).
  5. Ensure that the lower pane view is visible and that it displays the process handles (press CTRL+H for convenience, or open the pane from the View menu).

  1. Find the \BaseNamedObjects\AlertServiceEvent event in the handle list, right-click it and click Properties.
  2. In the Properties dialog box, select the Security tab. Note that the only security groups that have access to the event are the SYSTEM group and the Administrators group.

Task 3 - Modify the Service to Provide Security Attributes (DACL and SACL) for the Object

As part of this task, you will modify the service so that it properly sets access control rights (DACL and SACL) on the event object, making it accessible to its client.

  1. Repeat steps 1-4 from Task 2.
  2. In the file, find the //TODO comment marked with “STEP 2.”
  3. Find the active session ID and the user token associated with it using the WTSGetActiveConsoleSessionId and WTSQueryUserToken functions. Insert the following code:

C++

DWORD dwSessionID = WTSGetActiveConsoleSessionId();

HANDLE hToken = NULL;

if (WTSQueryUserToken(dwSessionID, &hToken) == FALSE)

{

goto Cleanup;

}

  1. Use the GetTokenInformation function to retrieve the user account SID (security identifier).
    Note that two passes are required – one to determine the size of the TOKEN_USER structure and another to actually fill it in. Insert the following code:

C++

DWORD dwLength;

TOKEN_USER* account = NULL;

if (GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) == FALSE &

GetLastError() != ERROR_INSUFFICIENT_BUFFER)

{

goto Cleanup;

}

account = (TOKEN_USER*)new BYTE[dwLength];

if (GetTokenInformation(hToken, TokenUser, (LPVOID)account, dwLength, &dwLength) == FALSE)

{

goto Cleanup;

}

  1. Use the ConvertSidToStringSid function to convert the user account SID to its string representation and from it construct an SDDL string that represents the security descriptor of the event that the service creates later. Insert the following code:

C++

LPWSTR lpszSid = NULL;

if (ConvertSidToStringSid(account->User.Sid, &lpszSid) == FALSE)

{

goto Cleanup;

}

WCHAR sddl[1000];

wsprintf(sddl, L"O:SYG:BAD:(A;;GA;;;SY)(A;;GA;;;%s)S:(ML;;NW;;;ME)", lpszSid);

  1. Convert the SDDL security descriptor string to a security descriptor using the ConvertStringSecurityDescriptorToSecurityDescriptor function. Insert the following code:

C++

PSECURITY_DESCRIPTOR sd = NULL;

if (ConvertStringSecurityDescriptorToSecurityDescriptor(sddl, SDDL_REVISION_1, &sd, NULL) == FALSE)

{

goto Cleanup;

}

  1. Initialize a SECURITY_ATTRIBUTES structure with the security descriptor created in step 6 and replace the line to create the event with the following code:

C++

SECURITY_ATTRIBUTES sa;

sa.bInheritHandle = FALSE;

sa.lpSecurityDescriptor = sd;

sa.nLength = sizeof(sa);

g_hAlertEvent = CreateEvent(&sa, FALSE, FALSE, L"Global\\AlertServiceEvent");

if (g_hAlertEvent == NULL)

{

goto Cleanup;

}

  1. Locate the //TODO comment marked with “STEP 3” and insert the following code to free the resources required to initialize the event:

C++

Cleanup:

if (hToken != NULL)

CloseHandle(hToken);

if (account != NULL)

delete[] account;

if (lpszSid != NULL)

LocalFree(lpszSid);

if (sd != NULL)

LocalFree(sd);

if (g_hAlertEvent == NULL)

CloseHandle(g_hAlertEvent);

  1. Build the solution.
  2. Repeat steps 7-13 from Task 1. Note that the client succeeds to open the event and receive notifications from the AlertService service.
  3. Repeat steps 9-13 from Task 2. Note that this time, the event grants access to the currently active user, which is why the AlertService client is able to open the event even though it does not have administrative privileges on the system.

Watch out

The security descriptor string (SDDL) used in this sample does not represent security best practices. In your applications, ensure that you apply the tightest security to resources shared by services and applications, and perform threat analysis and modeling to ensure that you have not created a security hole.

Exercise 3: Securing a File Object

In this exercise, you will install and run a service that creates a log file that should be accessible to the user. However, the user will be unable to write to or delete the file unless the service sets the appropriate security attributes, including the integrity level, which will allow the user to access the file.

Task 1 -Install and Run the Service

As part of this task, you will install the service using the installutil command line utility and then run it for the first time. You will see that the user is receiving an “Access Denied” error when attempting to delete the file created by the service.