A Process for Determining Appropriate Privilege

A Process for Determining Appropriate Privilege

In Chapter 4, I made a comment about being able to account for each ACE in an ACL; the same applies to SIDs and privileges in a token. If your application requires that you run as an administrator, you need to vouch for each SID and privilege in the administrator s token. If you cannot, you should consider removing some of the token entries.

Here s a process you can use to help determine, based on the requirements of your application, whether each SID and privilege should be in a token:

  1. Find out each resource the application uses.

  2. Find out each privileged API the application calls.

  3. Evaluate the account under which the application is required to run.

  4. Ascertain the SIDs and privileges in the token.

  5. Determine which SIDs and privileges are required to perform the application tasks.

  6. Adjust the token to meet the requirements in the previous step.

Step 1: Find Resources Used by the Application

The first step is draw up a list of all the resources used by the application: files, registry keys, Active Directory data, named pipes, sockets, and so on. You also need to establish what kind of access is required for each of these resources. For example, a sample Windows application that I ll use to illustrate the privilege-determining process utilizes the resources described in Table 5-2.

Table 5-2 Resources Used by a Fictitious Application

Resource

Access Required

Configuration data

Administrators need full control, as they must configure the application. All other users can only read the data.

Incoming data on a named pipe

Everyone must use the pipe to read and write data.

The data directory that the application writes files to

Everyone can create files and do anything to their own data. Everyone can read other users files.

The program directory

Everyone can read and execute the application. Administrators can install updates.

Step 2: Find Privileged APIs Used by the Application

Analyze which, if any, privileged APIs are used by the application. Examples include those in Table 5-3.

Table 5-3 Windows Functions and Privileges Required

Function Name

Privilege or Group Membership Required

CreateFile with FILE_FLAG_BACKUP_SEMANTICS

SeBackupPrivilege

LogonUser

SeTcbPrivilege (Windows XP does not require this)

ExitWindowsEx

SeShutdownPrivilege

OpenEventLog using the security event log

SeSecurityPrivilege

BroadcastSystemMessage[Ex] to all desktops (BSM_ALLDESKTOPS)

SeTcbPrivilege

RegisterLogonProcess

SeTcbPrivilege

InitiateShutdown

SeShutdownPrivilege

Debug functions, when debugging a process running as a different account than the caller, including DebugActiveProcess and ReadProcessMemory

SeDebugPrivilege

CreateProcessAsUser

SeIncreaseQuotaPrivilege and usually SeAssignPrimaryTokenPrivilege

CreatePrivateObjectSecurityEx

SeSecurityPrivilege

SetSystemTime

SeSystemtimePrivilege

VirtualLock and AllocateUserPhysicalPages

SeLockMemoryPrivilege

Net APIs such as NetUserAdd and NetLocalGroupDel

For many calls, caller must be a member of certain groups such as Administrators or Account Operators.

NetJoinDomain

SeMachineAccountPrivilege

SetProcessInformation

SeAssignPrimaryTokenPrivilege

note

Your application might call Windows functions indirectly through wrapper functions or COM interfaces. Make sure you take this into account.

In our sample Windows-based application, no privileged APIs are used. For most Windows-based applications, this is the case.

Step 3: Which Account Is Required?

Write down the account under which you require the application to run. For example, determine whether your application requires an administrator account to run or whether your service requires the local system account to run.

For our sample Windows application, development was lazy and determined that the application would work only if the user were an administrator. The testers were equally lazy and never tested the application under anything but an administrator account. The designers were equally to blame they listened to development and the testers!

Step 4: Get the Token Contents

Next ascertain the SIDs and privileges in the token of the account determined above. You can do this either by logging on as the account you want to test or by using the RunAs command to start a new command shell. For example, if you require your application to run as an administrator, you could enter the following at the command line:

RunAs /user:MyMachine\Administrator cmd.exe

This would start a command shell as the administrator assuming you know the administrator password and any application started in that shell would also run as an administrator.

If you are an administrator and you want to run a shell as SYSTEM, you can use the task scheduler service command to schedule a task one minute in the future. For example, assuming the current time is 5:01 P.M. (17:01 using the 24-hour clock), the following will start a command shell no more than one minute in the future:

At 17:02 /INTERACTIVE "cmd.exe"

The newly created command shell runs in the local system account context.

Now that you are running as the account you are interested in, run the following test code, named MyToken.cpp, from within the context of the account you want to interrogate. This code will display various important information in the user s token.

/* MyToken.cpp */ #include "Stdafx.h" #define MAX_NAME 256 // This function determines memory required // and allocates it. The memory must be freed by caller. LPVOID AllocateTokenInfoBuffer( HANDLE hToken, TOKEN_INFORMATION_CLASS InfoClass, DWORD *dwSize) { *dwSize=0; GetTokenInformation( hToken, InfoClass, NULL, *dwSize, dwSize); return new BYTE[*dwSize]; } // Get user from token. void GetUser(HANDLE hToken) { DWORD dwSize = 0; TOKEN_USER *pUserInfo = (TOKEN_USER *) AllocateTokenInfoBuffer(hToken, TokenUser, &dwSize); if (!pUserInfo) { printf("AllocateTokenInfoBuffer failed %u\n", GetLastError()); return; } if (!GetTokenInformation( hToken, TokenUser, pUserInfo, dwSize, &dwSize)) printf("GetTokenInformation failed %u\n", GetLastError()); SID_NAME_USE SidType; char lpName[MAX_NAME]; char lpDomain[MAX_NAME]; if (!LookupAccountSid( NULL, pUserInfo->User.Sid, lpName, &dwSize, lpDomain, &dwSize, &SidType) ) { if (GetLastError() == ERROR_NONE_MAPPED) strcpy( lpName, "NONE_MAPPED" ); else printf("LookupAccountSid Error %u\n", GetLastError()); } printf("\t%s\\%s\n", lpDomain, lpName); delete [] (LPBYTE) pUserInfo; } // Display SIDs and Restricting SIDs. void GetAllSIDs(HANDLE hToken, TOKEN_INFORMATION_CLASS tic) { DWORD dwSize = 0; TOKEN_GROUPS *pSIDInfo = (PTOKEN_GROUPS) AllocateTokenInfoBuffer( hToken, tic, &dwSize); if (!pSIDInfo) return; if (!GetTokenInformation(hToken, tic, pSIDInfo, dwSize, &dwSize)) printf("GetTokenInformation Error %u\n", GetLastError()); if (!pSIDInfo->GroupCount) printf("\tNone!\n"); for (DWORD i=0; i < pSIDInfo->GroupCount; i++) { SID_NAME_USE SidType; char lpName[MAX_NAME]; char lpDomain[MAX_NAME]; DWORD dwNameSize = MAX_NAME; DWORD dwDomainSize = MAX_NAME; DWORD dwAttr = 0; if (!LookupAccountSid( NULL, pSIDInfo->Groups[i].Sid, lpName, &dwNameSize, lpDomain, &dwDomainSize, &SidType)) { if (GetLastError() == ERROR_NONE_MAPPED) strcpy(lpName, "NONE_MAPPED"); else printf("LookupAccountSid Error %u\n", GetLastError()); } else dwAttr = pSIDInfo->Groups[i].Attributes; printf("%12s\\%-20s\t%s\n", lpDomain, lpName, (dwAttr & SE_GROUP_USE_FOR_DENY_ONLY) ? "[DENY]" : ""); } delete [] (LPBYTE) pSIDInfo; } // Display privileges. void GetPrivs(HANDLE hToken) { DWORD dwSize = 0; TOKEN_PRIVILEGES *pPrivileges = (PTOKEN_PRIVILEGES) AllocateTokenInfoBuffer(hToken, TokenPrivileges, &dwSize); if (!pPrivileges) return; BOOL bRes = GetTokenInformation( hToken, TokenPrivileges, pPrivileges, dwSize, &dwSize); if (FALSE == bRes) printf("GetTokenInformation failed\n"); for (DWORD i=0; i < pPrivileges->PrivilegeCount; i++) { char szPrivilegeName[128]; DWORD dwPrivilegeNameLength=sizeof(szPrivilegeName); if (LookupPrivilegeName(NULL, &pPrivileges->Privileges[i].Luid, szPrivilegeName, &dwPrivilegeNameLength)) printf("\t%s (%lu)\n", szPrivilegeName, pPrivileges->Privileges[i].Attributes); else printf("LookupPrivilegeName failed - %lu\n", GetLastError()); } delete [] (LPBYTE) pPrivileges; } int wmain( ) { if (!ImpersonateSelf(SecurityImpersonation)) { printf("ImpersonateSelf Error %u\n", GetLastError()); return -1; } HANDLE hToken = NULL; // Note that the token could come from the process, // in which case you would call OpenProcess or OpenProcessToken. if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken )) { printf( "OpenThreadToken Error %u\n", GetLastError()); return -2; } printf("\nUser\n"); GetUser(hToken); printf("\nSIDS\n"); GetAllSIDs(hToken, TokenGroups); printf("\nRestricting SIDS\n"); GetAllSIDs(hToken, TokenRestrictedSids); printf("\nPrivileges\n"); GetPrivs(hToken); RevertToSelf(); CloseHandle(hToken); return 0; }

You can also find this sample code on the companion CD in the folder Secureco\Chapter 5. The code opens the current thread token and queries that token for the user s name and the SIDs, restricting SIDs, and privileges in the thread. The GetUser, GetAllSIDs, and GetPrivs functions perform the main work. There are two versions of GetAllSIDs, one to get SIDs and the other to get restricting SIDs. Restricting SIDs are those SIDs in an optional list of SIDs added to an access token to limit a process s or thread s access to a level lower than that to which the user is allowed. I ll discuss restricted tokens later in this chapter. A SID marked for deny, which I ll discuss later, has the word [DENY] after the SID name.

note

You need to impersonate the user before opening a thread for interrogation. You do not need to perform this step if you call OpenProcessToken, however.

If you don t want to go through the exercise of writing code to investigate token contents, you can use the Token Master tool, originally included with Programming Server-Side Applications for Microsoft Windows 2000 (Microsoft Press, 2000), by Jeff Richter and Jason Clark, and included on the CD accompanying this book. This tool allows you to log on to an account on the computer and investigate the token created by the operating system. It also lets you access a running process and explore its token contents. Figure 5-2 shows the tool in operation.

Figure 5-2

Spelunking the token of a copy of Cmd.exe running as SYSTEM.

Scrolling through the Token Information field will give you a list of all SIDs and privileges in the token, as well as the user SID. For our sample application, the application is required to run as an administrator. The default contents of an administrator s token include the following, as determined by MyToken.cpp:

User NORTHWINDTRADERS\blake SIDS NORTHWINDTRADERS\Domain Users \Everyone BUILTIN\Administrators BUILTIN\Users NT AUTHORITY\INTERACTIVE NT AUTHORITY\Authenticated Users Restricting SIDS None Privileges SeChangeNotifyPrivilege (3) SeSecurityPrivilege (0) SeBackupPrivilege (0) SeRestorePrivilege (0) SeSystemtimePrivilege (0) SeShutdownPrivilege (0) SeRemoteShutdownPrivilege (0) SeTakeOwnershipPrivilege (0) SeDebugPrivilege (0) SeSystemEnvironmentPrivilege (0) SeSystemProfilePrivilege (0) SeProfileSingleProcessPrivilege (0) SeIncreaseBasePriorityPrivilege (0) SeLoadDriverPrivilege (2) SeCreatePagefilePrivilege (0) SeIncreaseQuotaPrivilege (0) SeUndockPrivilege (2) SeManageVolumePrivilege (0) 

Note the numbers after the privilege names. This is a bitmap of the possible values described in Table 5-4.

Table 5-4 Privilege Attributes

Attribute

Value

Comments

SE_PRIVILEGE_USED_FOR_ACCESS

0

The privilege was used to gain access to an object.

SE_PRIVILEGE_ENABLED_BY_DEFAULT

1

The privilege is enabled by default.

SE_PRIVILEGE_ENABLED

2

The privilege is enabled.

Step 5: Are All the SIDs and Privileges Required?

Here s the fun part: have members from the design, development, and test teams analyze each SID and privilege in the token and determine whether each is required. This task is performed by comparing the list of resources and used APIs found in steps 1 and 2 against the contents of the token from step 4. If SIDs or privileges in the token do not have corresponding requirements, you should consider removing them.

note

Some SIDs are quite benign, such as Users and Everyone. You shouldn t need to remove these from the token.

In our sample application, we find that the application is performing ACL checks only, not privilege checks, but the list of unused privileges is huge! If your application has a vulnerability that allows an attacker s code to execute, it will do so with all these privileges. Of the privileges listed, the debug privilege is probably the most dangerous, for all the reasons listed earlier in this chapter.

Step 6: Adjust the Token

The final step is to reduce the token capabilities, which you can do in two ways:

  • Allow less-privileged accounts to run your application.

  • Use restricted tokens.

Let s look at each in detail.

Allow Less-Privileged Accounts to Run Your Application

You can allow less-privileged accounts to run your application but not allow them to perform certain features. For example, your application might allow users to perform 95 percent of the tasks in the product but not allow them to, say, perform backups.

note

You can check whether the account using your application holds a required privilege at run time by calling the PrivilegeCheck function in Windows. If you perform privileged tasks, such as backup, you can then disable the backup option to prevent the user who does not hold the privilege from performing these tasks.

important

If your application requires elevated privileges to run, you might have corporate adoption problems for your application. Large companies don t like their users to run with anything but basic user capabilities. This is both a function of security and total cost of ownership. If a user can change parts of his systems because he has privilege to do so, he might get into trouble and require a call to the help desk. In short, elevated privilege requirements might be a deployment blocker for you.

One more aspect of running with least privilege exists: sometimes applications are poorly designed and require elevated privileges when they are not really needed. Often, the only way to rectify this sad state of affairs is to rearchitect the application.

I once reviewed a Web-based product that mandated that it run as SYSTEM. The product s team claimed this was necessary because part of their tool allowed the administrator of the application to add new user accounts. The application was monolithic, which required the entire process to run as SYSTEM, not just the administration portion. As it turned out, the user account feature was rarely used. After a lengthy discussion, the team agreed to change the functionality in the next release. The team achieved this in the following ways:

  • By running the application as a predefined lesser-privileged account instead of as the local system account.

  • By making the application require that administrators authenticate themselves by using Windows authentication.

  • By making the application impersonate the user account and attempt to perform user account database operations. If the operating system denied access, the account was not an administrator!

The new application is simpler in design and leverages the operating system security, and the entire process runs with fewer privileges, thereby reducing the chance of damage in the event of a security compromise.

From a security perspective, there is no substitute for an application running as a low-privilege account. If a process runs as SYSTEM or some other high-privilege account and the process impersonates the user to dumb down the thread s capabilities, an attacker might still be able to gain SYSTEM rights by injecting code, say through a buffer overrun, that calls RevertToSelf, at which point the thread stops impersonating and reverts to the process identity, SYSTEM. If an application always runs in a low-level account, RevertToSelf is less effective. A great example of this is in IIS 5. You should always run Web applications out of process (Medium and High isolation settings), which runs the application as the low-privilege IWAM_machinename account, rather than run the application in process with the Web server process (Low isolation setting), which runs as SYSTEM. In the first scenario, the potential damage caused by a buffer overrun is reduced because the process is a guest account, which can perform few privileged operations on the computer. Note also that in IIS 6 no user code runs as SYSTEM; therefore, your application will fail to run successfully if it relies on the Web server process using the SYSTEM identity.

Use Restricted Tokens

A new feature added to Windows 2000 and later is the ability to take a user token and dumb it down, or restrict its capabilities. A restricted token is a primary or impersonation token that the CreateRestrictedToken function has modified. A process or thread running in the security context of a restricted token is restricted in its ability to access securable objects or perform privileged operations. You can perform three operations on a token with this function to restrict the token:

  • Removing privileges from the token

  • Specifying a list of restricting SIDs

  • Applying the deny-only attribute to SIDs

Removing Privileges

Removing privileges is straightforward; it simply removes any privileges you don t want from the token, and they cannot be added back. To get the privileges back, the thread must be destroyed and re-created.

Specifying Restricting SIDs

By adding restricting SIDs to the access token, you can decide which SIDs you will allow in the token. When a restricted process or thread attempts to access a securable object, the system performs access checks on both sets of SIDs: the enabled SIDs and the list of restricting SIDs. Both checks must succeed to allow access to the object.

Let s look at an example of using restricting SIDs. An ACL on a file allows Everyone to read the file and Administrators to read, write, and delete the file. Your application does not delete files; in fact, it should not delete files. Deleting files is left to special administration tools also provided by your company. The user, Brian, is an administrator and a marketing manager. The token representing Brian has the following SIDs:

  • Everyone

  • Authenticated Users

  • Administrators

  • Marketing

Because your application does not perform any form of administrative function, you choose to incorporate a restricting SID made up of only the Everyone SID. When a user uses the application to manipulate the file, the application creates a restricted token. Brian attempts to delete the file by using the administration tool, so the operating system performs an access check by determining whether Brian has delete access based on the first set of SIDs. He does because he s a member of the Administrators group and administrators have delete access to the file. However, the operating system then looks at the next set of SIDs, the restricting SIDs, and finds only the Everyone SID there. Because Everyone has only read access to the file, Brian is denied delete access to the file.

note

The simplest way to think about a restricted SID is to think of ANDing the two SID lists and performing an access on the result. Another way of thinking about it is to consider the access check being performed on the intersection of the two SID lists.

Applying a Deny-Only Attribute to SIDs

Deny-only SIDs change a SID in the token such that it can be used only to deny the account access to a secured resource. It can never be used to allow access to an object. For example, a resource might have a Marketing (Deny All Access) ACE associated with it, and if the Marketing SID is in the token, the user is denied access. However, if another resource contains a Marketing (Allow Read) ACE and if the Marketing SID in the users token is marked for deny access, only the user will not be allowed to read the object.

I know it sounds horribly complex. Hopefully, Table 5-5 will clarify matters.

Table 5-5 Deny-Only SIDs and ACLs Demystified

Object ACL Contains Marketing (Allow Read) ACE

Object ACL Contains Marketing (Deny All Access) ACE

Object ACL Does Not Contain a Marketing ACE

User s token includes Marketing SID

Allow access

Deny access

Access depends on the other ACEs on the object

User s token includes the deny only Marketing SID

Deny access

Deny access

Access depends on the other ACEs on the object

Note that simply removing a SID from a token can lead to a security issue, and that s why the SIDs can be marked for deny-only. Imagine that an ACL on a resource denies Marketing access to the resource. If your code removes the Marketing SID from a user s token, the user can magically access the resource! Therefore, the SIDs ought to be marked for deny-only, rather than having the SID removed.

When to Use Restricted Tokens

When deciding when to use a restricted token, consider these issues:

  • If you know a certain level of access is never needed by your application, you can mark those SIDs for deny-only. For example, screen savers should never need administrator access. So mark those SIDs for deny-only. In fact, this is what the screen savers in Windows 2000 and later do.

  • If you know the set of users and groups that are minimally necessary for access to resources used by your application, use restricted SIDs. For example, if Authenticated Users is sufficient for accessing the resources in question, use Authenticated Users for the restricted SID. This would prohibit rogue code running under this restricted token from accessing someone s private profile data (such as cryptographic keys) because Authenticated Users is not on the ACL.

  • If your application loads arbitrary code, you should consider using a restricted token. Examples of this include e-mail programs (attachments) and instant messaging and chat programs (file transfer). If your application calls ShellExecute or CreateProcess on arbitrary files, you might want to consider using a restricted token.

Restricted Token Sample Code

Restricted tokens can be passed to CreateProcessAsUser to create a process that has restricted rights and privileges. These tokens can also be used in calls to ImpersonateLoggedOnUser or SetThreadToken, which lets the calling thread impersonate the security context of a logged-on user represented by a handle to the restricted token.

The following sample code outlines how to create a new restricted token based on the current process token. The token then has every privilege removed, with the exception of SeChangeNotifyPrivilege, which is required by all accounts in the system. The DISABLE_MAX_PRIVILEGE flag performs this step; however, you can create a list of privileges to delete if you want to remove specific privileges. Also, the local administrator s SID is changed to a deny-only SID.

/* Restrict.cpp */ // Create a SID for the BUILTIN\Administrators group. BYTE sidBuffer[256]; PSID pAdminSID = (PSID)sidBuffer; SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_NT_AUTHORITY; If (!AllocateAndInitializeSid( &SIDAuth, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &pAdminSID) ) { printf( "AllocateAndInitializeSid Error %u\n", GetLastError() ); return -1; } // Change the local administrator's SID to a deny-only SID. SID_AND_ATTRIBUTES SidToDisable[1]; SidToDisable[0].Sid = pAdminSID; SidToDisable[0].Attributes = 0; // Get the current process token. HANDLE hOldToken = NULL; if (!OpenProcessToken( GetCurrentProcess(), TOKEN_ASSIGN_PRIMARY TOKEN_DUPLICATE TOKEN_QUERY TOKEN_ADJUST_DEFAULT, &hOldToken)) { printf("OpenProcessToken failed (%lu)\n", GetLastError() ); return -1; } // Create restricted token from the process token. HANDLE hNewToken = NULL; if (!CreateRestrictedToken(hOldToken, DISABLE_MAX_PRIVILEGE, 1, SidToDisable, 0, NULL, 0, NULL, &hNewToken)) { printf("CreateRestrictedToken failed (%lu)\n", GetLastError() ); return -1; } if (pAdminSID) FreeSid(pAdminSID); // The following code creates a new process // with the restricted token. PROCESS_INFORMATION pi; STARTUPINFO si; ZeroMemory(&si, sizeof(STARTUPINFO) ); si.cb = sizeof(STARTUPINFO); si.lpDesktop = NULL; // Build the path to Cmd.exe to make sure // we're not running a Trojaned Cmd.exe. char szPath[MAX_PATH+1]/ cmd.exe , szSysDir[MAX_PATH+1]; if (GetSystemDirectory(szSysDir, sizeof szSysDir)) _snprintf(szPath, sizeof szPath, "%s\\cmd.exe", szSysDir); if (!CreateProcessAsUser( hNewToken, szPath, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) printf("CreateProcessAsUser failed (%lu)\n", GetLastError() ); CloseHandle(hOldToken); CloseHandle(hNewToken); return 0;

note

If a token contains a list of restricted SIDs, it is prevented from authenticating across the network as the user. You can use the IsTokenRestricted function to determine whether a token is restricted.

important

Do not force STARTUPINFO.lpDesktop NULL in Restrict.cpp to winsta0\\default. If you do and the user is using Terminal Server, the application will run on the physical console, not in the Terminal Server session that it ran from.

The complete code listing is available on the companion CD in the folder Secureco\Chapter 5. The sample code creates a new instance of the command shell so that you can run other applications from within the shell to see the impact on other applications when they run in a reduced security context.

If you run this sample application and then view the process token by using the MyToken.cpp code that you can find on the companion CD, you get the following output. As you can see, the Administrators group SID has become a deny-only SID, and all privileges except SeChangeNotifyPrivilege have been removed.

User NORTHWINDTRADERS\blake SIDS NORTHWINDTRADERS\Domain Users \Everyone BUILTIN\Administrators [DENY] BUILTIN\Users NT AUTHORITY\INTERACTIVE NT AUTHORITY\Authenticated Users Restricting SIDS None Privileges SeChangeNotifyPrivilege (3) 

The following code starts a new process using a restricted token. You can do the same for an individual thread. The following code shows how to use a restricted token in a multithreaded application. The thread start function, ThreadFunc, removes all the privileges from the thread token, other than bypass traverse checking, and then calls DoThreadWork.

#include <windows.h> DWORD WINAPI ThreadFunc(LPVOID lpParam) { DWORD dwErr = 0; try { if (!ImpersonateSelf(SecurityImpersonation)) throw GetLastError(); HANDLE hToken = NULL; HANDLE hThread = GetCurrentThread(); if (!OpenThreadToken(hThread, TOKEN_ASSIGN_PRIMARY TOKEN_DUPLICATE TOKEN_QUERY TOKEN_ADJUST_DEFAULT TOKEN_IMPERSONATE, TRUE, &hToken)) throw GetLastError(); HANDLE hNewToken = NULL; if (!CreateRestrictedToken(hToken, DISABLE_MAX_PRIVILEGE, 0, NULL, 0, NULL, 0, NULL, &hNewToken)) throw GetLastError(); if (!SetThreadToken(&hThread, hNewToken)) throw GetLastError(); // DoThreadWork operates in restricted context. DoThreadWork(hNewToken); } catch(DWORD d) { dwErr = d; } if (dwErr == 0) RevertToSelf(); return dwErr; } void main() { HANDLE h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFunc, NULL, CREATE_SUSPENDED, NULL); if (h) ResumeThread(h);  }

Software Restriction Policies and Windows XP

Windows XP includes new functionality, named Software Restriction Policies also known as SAFER to make restricted tokens easier to use and to deploy in applications. I want to focus on the programmatic aspects of SAFER rather than on its administrative features. You can learn more about SAFER administration in the Windows XP online Help by searching for Software Restriction Policies.

SAFER also includes some functions, declared in Winsafer.h, to make working with reduced privilege tokens easier. One such function is SaferComputeTokenFromLevel. This function is passed a token and can change the token to match predefined reduced levels of functionality.

The following sample code shows how you can create a new process to run as NormalUser, which runs as a nonadministrative, non-power-user account. This code is also available on the companion CD in the folder Secureco\Chapter 5. After you run this code, run MyToken.cpp to see which SIDs and privileges are adjusted.

/* CreateSaferProcess.cpp */ #include <windows.h> #include <WinSafer.h> #include <winnt.h> #include <stdio.h> void main() { SAFER_LEVEL_HANDLE hAuthzLevel; // Valid programmatic SAFER levels: // SAFER_LEVELID_FULLYTRUSTED // SAFER_LEVELID_NORMALUSER // SAFER_LEVELID_CONSTRAINED // SAFER_LEVELID_UNTRUSTED // SAFER_LEVELID_DISALLOWED // Create a normal user level. if (SaferCreateLevel(SAFER_SCOPEID_USER, SAFER_LEVELID_NORMALUSER, 0, &hAuthzLevel, NULL)) { // Generate the restricted token that we will use. HANDLE hToken = NULL; if (SaferComputeTokenFromLevel( hAuthzLevel, // Safer Level handle NULL, // NULL is current thread token. &hToken, // Target token 0, // No flags NULL)) { // Reserved // Build the path to Cmd.exe to make sure // we're not running a Trojaned Cmd.exe. char szPath[MAX_PATH+1], szSysDir[MAX_PATH+1]; if (GetSystemDirectory(szSysDir, sizeof szSysDir)) { _snprintf(szPath, sizeof szPath, "%s\\cmd.exe", szSysDir); STARTUPINFO si; ZeroMemory(&si, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.lpDesktop = NULL; PROCESS_INFORMATION pi; if (!CreateProcessAsUser( hToken, szPath, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) printf("CreateProcessAsUser failed (%lu)\n", GetLastError() ); } } SaferCloseLevel(hAuthzLevel); } }

note

SAFER does much more than make it easier to create predefined tokens and run processes in a reduced context. Explaining the policy and deployment aspects of SAFER is beyond the scope of this book, a book about building secure applications, after all. However, even a well-written application can be subject to attack if it s poorly deployed or administered. It is therefore imperative that the people deploying your application understand how to install and manage technologies, such as SAFER, in a robust and usable manner.



Writing Secure Code
Writing Secure Code, Second Edition
ISBN: 0735617228
EAN: 2147483647
Year: 2005
Pages: 153

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