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.
- Using Visual Studio, open the Session0_Starter solution.
- Build the entire solution (make note of the build configuration you used – Debug/Release, x86/x64).
- Open an administrative command prompt:
- Click Start.
- Point to All Programs.
- Point to Accessories.
- Right-click Command Prompt.
- Click Run as administrator.
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:C:
cd C:\Session0_Starter\Debug
Issue the following command to create the TimeService servicesc 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=”).
- Open the Services MMC Snap-in by clicking +R and typing services.mscinto the Run dialog box.
Locate the TimeService service, right-click it, and click Start.
After a few seconds, you will see a dialog box similar to the following image.
- This is the Interactive services dialog detection dialog box, which detects a service attempting to display UI and presents this mitigation fix.
- 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).
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.
- If you haven’t done so yet, follow steps 1-5 in Task 1 to install the TimeService service.
- If you haven’t done so yet after completing Task 1, make sure to stop the TimeService service (see step 10 in Task 1).
- Using Visual Studio, open the Session0_Starter solution.
- Locate the TimeService project under the UI\Native solution folder and open the TimeService.cpp file.
Find the first //TODO comment in the file. Comment out the MessageBox function call and replace it with the following: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);
- Build the solution.
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.
- Click No to dismiss the message.
- 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.
- Repeat steps 1-4 from Task 2.
- Find the second //TODO comment in the TimeService.cpp file.
Begin with retrieving the active session ID and its related user token (see https://en.wikipedia.org/wiki/Token_(Windows_NT_architecture) 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:
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;
}
Duplicate the token so that it can be used to create a process, using the DuplicateTokenEx function. Insert the following code:
HANDLE hDuplicatedToken = NULL;
if (DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hDuplicatedToken) == FALSE)
{
goto Cleanup;
}
Create an environment block for the interactive process, using the CreateEnvironmentBlock function. Insert the following code:
LPVOID lpEnvironment = NULL;
if (CreateEnvironmentBlock(&lpEnvironment, hDuplicatedToken, FALSE) == FALSE)
{
goto Cleanup;
}
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:
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");
Create the process under the target user’s context using the CreateProcessAsUser function. Insert the following code:
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;
Make sure you have code in place to free resources allocated during this work. Insert the following code:
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);
- Build the solution.
- 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.
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.
|
|