Reducing Privileges


An ordinary process has a couple of options in terms of reducing unneeded privileges; for example, the process could call AdjustTokenPrivileges on initialization, with a parameter of SE_PRIVILEGE_REMOVED. Unfortunately, AdjustTokenPrivileges is one of the least usable API calls you could ever encounter. The first problem is that it can succeed without doing anything. Only if it succeeds and GetLastError returns ERROR_SUCCESS did it successfully adjust all the privileges you asked it to manipulate–this is even true if you only asked it to change one privilege. If it fails to adjust all the privileges, trying to figure out which privileges weren’t adjusted will mean some tricky code: the privileges that worked will end up being written into the parameter for the previous state. Another annoying aspect of AdjustTokenPrivileges, at least when used to disable privileges, is that it works backward from how a good security operation should behave: it disables known bad and doesn’t just enable known good. If you’re clever (and determined), you can write code to obtain the list of privileges available to the process, then remove only the privileges you’d like to keep from your list, and then remove the remaining privileges.

A second approach would be to try and run the service using a restricted token. Unfortunately, you don’t control the SCM code that starts the service process and trying to do this by hand would be difficult and error-prone. Then there’s the fact that CreateRestrictedToken is difficult to use; you’re likely to encounter problems, such as being able to create objects that your process doesn’t have enough access to; and you could have problems with COM objects. Additionally, you have the same problem as with AdjustTokenPrivileges in that the API asks for the privileges you’d like to disable, not the privileges you’d like to keep. We strongly recommend not taking the CreateRestrictedToken approach in a service, although it may be useful if your service has to launch helper processes, which is what we did with the Windows Search feature shipping in Vista. Even so, we’ve had a number of learning experiences along the way.

Fortunately, a new information level was created for the ChangeServiceConfig2 API, and we have a relatively easy way to specify the privileges the service needs. Even better, the API takes the list of privileges you need–enable known good, never try to disable known bad. You’ll always forget something, or they’ll invent a new, dangerous privilege. It even gets better! The way that this works in the operating system is that the privileges are kept as a multistring in the registry under the RequiredPrivileges value, so you could tinker with these on the fly without recompiling your application by using the sc privs command. Our only complaint is that multistrings are a little tricky, and we’d have preferred just passing in a TOKEN_PRIVILEGES structure, but since you can just use our sample code, this isn’t really a problem. Here’s the sample:

 DWORD SetServicePrivs( SC_HANDLE hSvc, const WCHAR* wzPrivs[], DWORD cPrivs ) { // The call to set the privileges for a service requires you to pack // them into a multi-string, which is a series of strings in the same // buffer that have a double-null terminator size_t cchTotal = 0; DWORD i; for( i = 0; i < cPrivs; i++ ) {     // Add up the length of all the input strings, including the null     cchTotal += wcslen( wzPrivs[i] ) + 1; } // Add one more for the final null cchTotal++; SERVICE_REQUIRED_PRIVILEGES_INFO ReqPrivs; const WCHAR wzMultiStringNull[2] = { '\0', '\0' }; if( cchTotal > 1 )    ReqPrivs.pmszRequiredPrivileges = new WCHAR[ cchTotal ]; else    ReqPrivs.pmszRequiredPrivileges = wzMultiStringNull; WCHAR* wzTmp = ReqPrivs.pmszRequiredPrivileges; if( wzTmp != NULL ) {     DWORD dwErr = ERROR_SUCCESS;     if( cchTotal > 1 )     {        for( i = 0; i < cPrivs; i++ )        {            size_t cchPriv = wcslen( wzPrivs[i] ) + 1;            wcscpy_s( wzTmp, cchTotal, wzPrivs[i] );            wzTmp += cchPriv;            cchTotal -= cchPriv;        }             // Set the final null            *wzTmp = '\0';        }                // Now we can set the privilege        if( !ChangeServiceConfig2( hSvc,                                   SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO,                                   &ReqPrivs ) )        {            dwErr = GetLastError();        }        if( cchTotal > 1 )            delete[] ReqPrivs.pmszRequiredPrivileges;        return dwErr;     }     return ERROR_NOT_ENOUGH_MEMORY; }

You can call the function like this:

 const WCHAR* wzPrivs[] = { SE_CHANGE_NOTIFY_NAME,                            SE_CREATE_GLOBAL_NAME }; DWORD dwRet = SetServicePrivs( hSvc, wzPrivs, _countof( wzPrivs ) );

Note that this service didn’t really require the right to create named objects in the global namespace, but it was a benign privilege we could add to prove that the code actually works as advertised with multiple privileges. If you had given an empty list of privileges, you’d still end up with SE_CHANGE_NOTIFY_NAME (right to bypass traverse checking) enabled, because it is a benign right, and a lot of things will break if you don’t have it enabled. Here’s a list of privileges currently defined, along with some commentary specific to services.

High-Level Privileges

Don’t enable any of these privileges unless you have a need to do so, and understand why you’re doing it. If you have to have any high-level privileges, try to use them during initialization, and then call AdjustTokenPrivileges to drop them once you’ve completed initialization.

  • SeAssignPrimaryTokenPrivilege, which is required to assign the primary process token. In some circumstances, you may need this when calling CreateProcessAsUser–see the SDK documentation. The user-friendly text is, “Replace a process-level token.”

  • SeAuditPrivilege, which is needed to generate entries in the security log. The security log now has configurable ACLs, which may be a better approach than assigning this privilege. The user-friendly test is, “Generate security audits.”

  • SeBackupPrivilege, which essentially disables access checking on files for the entire process. Unless you’re writing backup software, don’t ever enable this privilege. The user-friendly text is, “Back up files and directories.”

  • SeCreatePermanentPrivilege, which is needed to create permanent devices under the \\Device namespace. This right isn’t normally required by user-mode applications and shouldn’t be granted to your service. The user-friendly string is “Create permanent shared objects.”

  • SeCreateTokenPrivilege, which is required to create a primary token. This privilege also seems to have no associated API calls, except NtCreateToken, which isn’t documented or supported in user-mode code. The user-friendly string is, “Create a token object.”

  • SeDebugPrivilege, which allows a process to open any other process for debugging. Any process with this privilege can compromise the operating system and should never be enabled in any service, except an actual debugger. The user-friendly string is, “Debug programs.”

  • SeEnableDelegationPrivilege, which is typically used to delegate domain administration tasks. It’s unlikely that you’d be writing a service that needed this privilege, and it’s only meaningful on a domain controller. Any process with this right enabled could have a tremendous effect on the security of the domain. Don’t enable this unless you’re writing an application to help manage domains. The user-friendly string is, “Enable computer and user accounts to be trusted for delegation.”

  • SeLoadDriverPrivilege, which is needed to load and unload device drivers. A device driver can completely compromise the operating system, and this privilege shouldn’t be enabled for any typical service. The user-friendly name is, “Load and unload device drivers.”

  • SeManageVolumePrivilege, which is used to call some volume management APIs. If your service needs this API, leave it enabled. The user-friendly string is, “Manage the files on a volume.”

  • SeRestorePrivilege, which is the companion to the SeBackupPrivilege. It can be used to overwrite any file, without regard to ACL, and allows you to change the owner of a file. This is one of the more dangerous privileges and shouldn’t be enabled under most circumstances. The user-friendly string is, “Restore files and directories.”

  • SeSecurityPrivilege, which is needed to set an SACL (system audit access check), and enables manipulation of system auditing parameters. Because this privilege has strong security implications, it is dangerous and should only be used when absolutely required. In general, it is preferable to use the configurable ACLs on the event logs to delegate the right to read events than by using this privilege. The user-friendly string is, “Manage auditing and security log.”

  • SeSyncAgentPrivilege, which is needed for a domain controller to synchronize the LDAP directory. A process with this privilege can read any object or property in the directory, regardless of ACL. Do not enable this unless absolutely required. The user-friendly name is “Synchronize directory service data.”

  • SeSystemEnvironment, which allows the process to manipulate system environment variables. A process with the ability to manipulate the system environment variables, notably the path settings, can have strong effects on the rest of the system. The user-friendly string is, “Modify firmware environment values.”

  • SeSystemtimePrivilege, which is required to modify the system time of day, although not the time zone, which is a change for Windows Vista. The system time has implications for Kerberos tickets, and unless you’re replacing the Windows Time service, don’t enable this privilege.

  • SeTakeOwnershipPrivilege, which is required to assume ownership of an object without having explicit access. This privilege is typically used to bypass ACLs in cases where someone has removed administrator access. Like the backup and restore privileges, this is obviously highly sensitive and shouldn’t be granted to ordinary services. The user-friendly name is, “Take ownership of files or other objects.”

  • SeTcbPrivilege, which allows a process to act as part of the operating system. Although the details are somewhat arcane, a process with this right can compromise the operating system and should never be granted to a typical service. Under Windows 2000, this privilege was required to call LogonUser, which led to several escalation of privilege points. On later version of Windows, you don’t need this privilege if you know the user’s password. The user-friendly string is, “Act as part of the operating system.”

Benign Privileges

The following privileges typically won’t allow someone to compromise the operating system, but only enable these if you need them. Many of these illustrate our point that disabling privileges you think may be bad can get you into trouble if you start running on a domain controller and find your service has privileges you hadn’t even known about.

  • SeChangeNotifyPrivilege, which is always enabled, even if you try to disable it. The user-friendly string is, “Bypass traverse checking.”

  • SeCreateGlobalPrivilege, which is needed to create objects in the global namespace. This right is granted by default to services and will be needed if the service creates named objects such as events, mutexes, or shared memory sections. If your service doesn’t create any named objects, you may be able to drop this privilege. The user-friendly string is, “Create global objects.”

  • SeCreatePagefilePrivilege, which is needed to create page files. There are no documented user-mode API calls that require this right; thus there’s no need for your service to enable this privilege. The user-friendly string is, “Create a pagefile.”

  • SeImpersonatePrivilege, which is needed to impersonate clients. The danger here is to the client, not the server. If you need to impersonate clients with APIs such as Impersonate-NamedPipeClient, then you’ll need this privilege enabled. Although as we previously noted, if all you’re doing is identifying the user, this privilege isn’t needed. As always, if you don’t require it, drop it. Our sample service, which does use named pipes but doesn’t impersonate, is much less dangerous without this privilege. Another item to think about is that if your service does impersonate clients, and if it can either regularly or forceably impersonate a local system process, you could provide an avenue for escalation of privilege unless you’re very careful. The user-friendly string is, “Imper-sonate a client after authentication.”

  • SeIncreaseBasePriorityPrivilege, which is needed to change the base priority of a process. Changing a process’s priority is usually a bad idea, but the worst you’d typically do with this one is find new meanings for “denial of service.” If you think you need this, think again, and if you still think you need it, enable it. The user-friendly string is, “Increase scheduling priority.”

  • SeIncreaseQuotaPrivilege, which is needed in some cases when calling CreateProcessAs-User and is needed by SetSystemFileCacheSize. The user-friendly name is, “Adjust memory quotas for a process.”

  • SeLockMemoryPrivilege, which is used to lock physical RAM. Unless you’re writing an application to diagnose physical memory problems, you probably don’t need this privilege enabled. The user-friendly string is, “Lock pages in memory.”

  • SeMachineAccountPrivilege, which is needed to join new computers to the domain. Unless your application is used to delegate domain administration tasks, you won’t need this privilege. The user-friendly string is, “Add workstations to domain.”

  • SeProfileSingleProcessPrivilege, which is needed to use some of the performance monitoring features. This privilege would typically have few security implications, but as always, don’t enable it unless required. The user-friendly string is, “Profile single process.”

  • SeRemoteShutdownPrivilege, which is needed to shut down a system from the network. This privilege shouldn’t be needed by a service. The user-friendly name is, “Force shutdown from a remote system.”

  • SeShutdownPrivilege, which is required to shut down the system. Your service typically won’t need to do this, but the implications are obvious. The user-friendly string is, “Shut down the system.”

  • SeSystemProfilePrivilege, which is needed to acquire profiling information for the system. Like SeProfileSingleProcessPrivilege, this privilege has few direct security implications, but it should only be enabled when needed. The user-friendly string is, “Profile system performance.”

  • SeUndockPrivilege, which is needed to undock a laptop. We have a hard time imagining how to compromise a system because of this privilege, but we have an equally hard time imagining a service that needs it. The user-friendly string is, “Remove computer from docking station.”

  • SeUnsolicitedInputPrivilege is obsolete; in fact, it was removed in Windows NT 3.5.



Writing Secure Code for Windows Vista
Writing Secure Code for Windows Vista (Best Practices (Microsoft))
ISBN: 0735623937
EAN: 2147483647
Year: 2004
Pages: 122

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