Designing Applications to Run at a Low Integrity Level
An easy way to run an application process at a low integrity level is to set the integrity level of the executable program file to low integrity. When that image file is launched, the application process is started with a low integrity level. For example, suppose we want to run the Windows Calculator application in a low integrity process.
To run calc.exe at low integrity
Make a copy of c:\Windows\system32\calc.exe to a temporary folder.
Use the icacls program to set the integrity level of the temporary file, lowcalc.exe, to low integrity using the icacls command:
icacls lowcalc.exe /setintegritylevel Low
Run the low-integrity version of calc.exe.
The following image shows the steps to run Windows Calculator in a low-integrity process.
Figure 9 Launching Windows Calculator at low integrity
You can use Process Explorer to confirm that the image file, lowcalc.exe, is indeed running at low integrity. The Integrity Level column is on the right side of the image.
Figure 10 Low calculator process
Not all application programs will run properly in a low-integrity process. A low integrity process does not have write access to most areas under the user’s local profile area of the file system or the registry under HKCU. The inability for a low-integrity process to get write access to the user profile is a good thing if the program is unwanted malicious software. But for applications like Protected Mode Internet Explorer, some redesign may be necessary to get all features of the application behaving correctly.
Use helpful tools such as Process Monitor from Sysinternals.com to get an idea of what file and registry resources an application currently uses for write access that will fail when running at low integrity.
While it is possible to change an application to run entirely at low integrity, some features of the application may work properly only when they are implemented in a medium-integrity process. An application that is running at low integrity may have one part of the application in a low-integrity process, for example to handle untrusted data from the Internet. Another part of the application can be implemented in a medium-integrity “broker” process to handle a small set of user-initiated actions. Communication between the low-integrity and medium-integrity processes in the application can be handled using various IPC mechanisms. The medium-integrity part of the application must assume that all data and code in the low-integrity process is untrustworthy.
Protected Mode Internet Explorer is one application that is redesigned to run in a low-integrity process. For more information about Protected Mode Internet Explorer, see Understanding and Working in Protected Mode Internet Explorer (https://go.microsoft.com/fwlink/?LinkId=90931).
The main topics for designing an application to run at low integrity are the following:
- Starting a child process at low integrity
- Writeable locations for low integrity applications
- Communication between low-integrity and higher-level processes
Starting a process at low integrity
By default, child processes inherit the integrity level of their parent process. To start a low-integrity process, you must start a new child process with a low-integrity access token using the function CreateProcessAsUser. To start a low-integrity process from a medium-integrity process, you must start the new process explicitly as low integrity.
To start a low-integrity process
Duplicate the handle of the current process, which is at medium integrity level.
Use SetTokenInformation to set the integrity level in the access token to Low.
Use CreateProcessAsUser to create a new process using the handle to the low integrity access token.
CreateProcessAsUser updates the security descriptor in the new child process and the security descriptor for the access token to match the integrity level of the low-integrity access token.
The following code sample demonstrates this process.
void CreateLowProcess()
{
BOOL fRet;
HANDLE hToken = NULL;
HANDLE hNewToken = NULL;
PSID pIntegritySid = NULL;
TOKEN_MANDATORY_LABEL TIL = {0};
PROCESS_INFORMATION ProcInfo = {0};
STARTUPINFO StartupInfo = {0};
// Notepad is used as an example
WCHAR wszProcessName[MAX_PATH] =
L"C:\\Windows\\System32\\Notepad.exe";
// Low integrity SID
WCHAR wszIntegritySid[20] = L"S-1-16-1024";
PSID pIntegritySid = NULL;
fRet = OpenProcessToken(GetCurrentProcess(),
TOKEN_DUPLICATE |
TOKEN_ADJUST_DEFAULT |
TOKEN_QUERY |
TOKEN_ASSIGN_PRIMARY,
&hToken);
if (!fRet)
{
goto CleanExit;
}
fRet = DuplicateTokenEx(hToken,
0,
NULL,
SecurityImpersonation,
TokenPrimary,
&hNewToken);
if (!fRet)
{
goto CleanExit;
}
fRet = ConvertStringSidToSid(wszIntegritySid, &pIntegritySid);
if (!fRet)
{
goto CleanExit;
}
TIL.Label.Attributes = SE_GROUP_INTEGRITY;
TIL.Label.Sid = pIntegritySid;
//
// Set the process integrity level
//
fRet = SetTokenInformation(hNewToken,
TokenIntegrityLevel,
&TIL,
sizeof(TOKEN_MANDATORY_LABEL) + GetLengthSid(pIntegritySid));
if (!fRet)
{
goto CleanExit;
}
//
// Create the new process at Low integrity
//
fRet = CreateProcessAsUser(hNewToken,
NULL,
wszProcessName,
NULL,
NULL,
FALSE,
0,
NULL,
NULL,
&StartupInfo,
&ProcInfo);
CleanExit:
if (ProcInfo.hProcess != NULL)
{
CloseHandle(ProcInfo.hProcess);
}
if (ProcInfo.hThread != NULL)
{
CloseHandle(ProcInfo.hThread);
}
LocalFree(pIntegritySid);
if (hNewToken != NULL)
{
CloseHandle(hNewToken);
}
if (hToken != NULL)
{
CloseHandle(hToken);
}
return fRet;
}
Writeable locations at low integrity
Windows Vista has specific file and registry locations that are assigned low mandatory labels to allow low-integrity applications write access. Table 10 shows these writeable locations.
Table 10 Locations that are writeable for low mandatory labels
Location | Writeable area |
---|---|
Registry |
Low-integrity processes can write to and create subkeys under HKEY_CURRENT_USER\Software\AppDataLow |
File system |
Low-integrity processes can write and create subfolders under %USER PROFILE%\AppData\LocalLow |
Lowering a resource mandatory label
Because of potential security risks, we do not recommend that you design a higher-integrity process to accept input or share resources with low-integrity processes. The low-integrity process might attempt malicious behavior. However, you may be required to do this action by design.
Note
Applications that accept input from or share resources with lower-integrity processes should assume that data that lower-integrity processes provide cannot be trusted, and should then perform appropriate validation. For example, Protected Mode Internet Explorer displays the Save As dialog box from the Internet Explorer User Broker process. This allows users to confirm that they want to save a file using a process that runs with higher rights than Protected Mode Internet Explorer.
Because low-integrity applications can write only to low-integrity resources, you need to lower the integrity level of the shared resources.
To lower the integrity level of the shared resources
Create an SDDL security descriptor that defines a low mandatory label.
Convert the SDDL string to a security descriptor.
Assign the low-integrity attribute to the security descriptor.
Assign the security descriptor to the shared resource.
The following code sample shows this process.
#include <sddl.h>
#include <AccCtrl.h>
#include <Aclapi.h>
void SetLowLabelToFile()
{
// The LABEL_SECURITY_INFORMATION SDDL SACL to be set for low integrity
#define LOW_INTEGRITY_SDDL_SACL_W L"S:(ML;;NW;;;LW)"
DWORD dwErr = ERROR_SUCCESS;
PSECURITY_DESCRIPTOR pSD = NULL;
PACL pSacl = NULL; // not allocated
BOOL fSaclPresent = FALSE;
BOOL fSaclDefaulted = FALSE;
LPCWSTR pwszFileName = L"Sample.txt";
if (ConvertStringSecurityDescriptorToSecurityDescriptorW(
LOW_INTEGRITY_SDDL_SACL_W, SDDL_REVISION_1, &pSD, NULL))
{
if (GetSecurityDescriptorSacl(pSD, &fSaclPresent, &pSacl,
&fSaclDefaulted))
{
// Note that psidOwner, psidGroup, and pDacl are
// all NULL and set the new LABEL_SECURITY_INFORMATION
dwErr = SetNamedSecurityInfoW((LPWSTR) pwszFileName,
SE_FILE_OBJECT, LABEL_SECURITY_INFORMATION,
NULL, NULL, NULL, pSacl);
}
LocalFree(pSD);
}
}
Application processes can set the integrity of securable objects only to levels at or below the integrity level of the application process.
Communication between low-integrity and higher-integrity processes
Low-integrity processes are not completely isolated from other applications. They can interact with other processes. In fact, without some forms of collaboration, applications running at low integrity may seem to the user to be completely broken.
Some forms of IPC are available for low-integrity processes to communicate with higher-integrity processes. Components in Windows Vista block the following types of communication.
Most window messages and process hooks are blocked by UIPI.
Opening a process and using CreateRemoteThread is blocked by the mandatory label on process objects.
Opening a shared memory section for write access is blocked.
Using a named object created by a higher integrity process for synchronization is blocked by the default mandatory label.
Binding to a running instance of a COM service is block.
However, you can use other types of communication between a low-integrity process and a higher-integrity process. The types of communication that you can use include:Clipboard (copy and paste)
Remote procedure call (RPC)
Sockets
Window messages that the higher-integrity process has been explicitly allowed to receive from lower-integrity processes by calling ChangeWindowMessageFilter
Shared memory, where the higher-integrity process explicitly lowers the mandatory label on the shared memory section
Important
This is particularly dangerous, and the higher-integrity process must be careful to validate all data that is written to the shared section.
COM interfaces, where the launch activation rights are set programmatically by the higher-integrity process to allow binding from low integrity clients
Named pipes, where the creator explicitly sets the mandatory label on the pipe to allow access to lower-integrity processes
These communication mechanisms allow the low-integrity process to interact with other application processes, such as a broker process, that are specifically designed to accept input or calls from the low-integrity source.
A general guideline for designing interfaces that a low-integrity process will call is never to trust the caller or the input data. A medium-integrity broker can provide an interface to create a file given a path and to allow a low-integrity application to call the interface. However, this defeats the purpose of running an app at low integrity. A better design is for a low-integrity process to call an interface that requests the medium-integrity application to present a common file dialog to the user, which the low-integrity process cannot drive using window messages. This way, you let the user browse and select what file to open or create, and the medium-integrity process does all the file operations. This type of Save As scenario is an example of how Protected Mode Internet Explorer uses its own broker process to handle saving a Web page somewhere in the user’s profile.
Many application features can run correctly in a low-integrity process. There is no common set of tools for running applications at low integrity. Each application is different, and not all applications need to run at low integrity.