Least privilege for services

This is part 2 of our series of posts on service hardening.

Executing with least privilege is a good practice of computer security. As with the "need to know" principle for information access, there should be a "need to have" principle for privileges. If your code does not need a specific privilege why should it run with it?

With Windows Vista, User Account Control ensures that user applications execute with less (and lower) privileges by default. User Account Control does not, however, apply to nor affect Win32 services which have their own mechanism (new to Windows Vista) for execution with least privileges. In Windows Vista and Longhorn Server, the RequiredPrivileges registry key allows to specify the privileges that the service should execute with. The process hosting the service will only have the privileges specified in RequiredPrivileges , and other privileges will be removed from the process token.

Although this may seem obvious, it is still worth stating that the RequiredPrivilege mechanism cannot in any case be used to augment the privileges of services but only to reduce them. If RequiredPrivileges refers to privileges that the service account does not already have, those privileges will be ignored. This is a recurring scheme in security by which a given mechanism can be used to restrain, but not to widen access.

 

As a developer you should determine what minimum privileges your service needs and configure your service during its installation to run with those privileges only. This is an initiative of "good citizenship" on your part to lower the potential damage to the system in the event your code gets compromised. You can configure your service for least privilege with the ChangeServiceConfig2 API. If you don't, the administrator can still do it using the sc.exe command:

sc privs [service name] [Privileges]

And the current configuration for a service can be queried with the qprivs verb:

sc qprivs [service name]

Whether you use the ChangeServiceConfig2 API or the sc.exe command, you specify a privilege by its string. For instance, for the impersonate privilege, you would use "SeImpersonatePrivilege". The Windows SDK and winnt.h has the list of those and corresponding constants for each privilege strings (look for "Privilege Constants").

How do you determine which privileges your service needs? To our knowledge, this has to be done manually (please correct us if profiling tools similar to the ones for UAC analysis are available). Fortunately, the Windows SDK documentation often mentions the privileges needed when working with a given API and also whether those privileges should be enabled before calling the API versus whether the API is taking care of that. You may need to read the Remarks section carefully though. For instance, the SDK documentation for ImpersonateLoggedOnUser states that "The calling thread does not need to have any particular privilege." Sure, the API can be called without any particular privilege for some scenarios such as "identification level" impersonation, but if you want to do real impersonation, you do need the impersonate privilege.

In practice we've found that eliminating the unneeded privileges using knowledge of what the service does, code reviews, SDK documentation, and testing all execution paths was not too expensive and well worth the effort.

If you are hosting several services in the same process, the set of privileges the process gets is the union of those of its services. You should pay attention to this and regroup services according to their privilege requirements.

 

Local Service and Network Service built-in service accounts were designed to be able to run services with lower privileges (than those of the all powerful Local System) without having to use dedicated user accounts. Creating and using dedicated user accounts for running services can be less than ideal as those user accounts have to be created and managed, including their (hopefully long and strong) passwords.

Local Service has about eleven privileges and no network identity. A service running under Local Service may listen for client requests from the network but if it attempts to reach a remote service, it will be seen as anonymous and most likely will fail (unless of course the remote service accepts anonymous requests). Network Service has one less privilege (i.e. it does not have the SeSystemTimePrivilege) and its network identity is the machine account on the domain.

Before Windows Vista, using Local or Network Service built-in accounts lacked granularity as all services running under a built-in account had the same privileges. Thanks to the RequiredPrivileges mechanism you can now have a fine granularity control over the privileges of your service without having recourse to dedicated user accounts. As a matter of fact, the other service hardening mechanisms in Windows Vista, which we will cover in our next posts, all tend to facilitate the use of built-in service accounts by providing more granular control. Several scenarii that previously reckoned for a dedicated user account are now possible with built-in accounts.

That said, there are plenty situations where a dedicated domain wide user account is necessary and fully appropriate for running a service. One such example is when you need to trust your service (such as trust for delegation) or grant access to your service on a remote resource or service, and you've determined that you’d rather trust your service specifically than trust the machine account.

 

Microsoft has gone at length in its effort to reduce privileges of native Win32 service in Windows Vista. You can use Process Explorer to see the processes hosting Windows Vista services. A lot of native services are hosted using instances of svchost.exe (more on how native Vista services are grouped in svchost instances in our next posts). Notice that the DHCP Client service is hosted in a svchost instance that runs under Local Service with only four privileges (compare this with Local System and its twenty or so privileges under Windows XP). Actually, the DHCP Client service only requires two privileges as shown by the sc command:

C:\Windows\System32>sc qprivs dhcp

[SC] QueryServiceConfig2 réussite(s)

SERVICE_NAME: dhcp

PRIVILEGES : SeChangeNotifyPrivilege

: SeCreateGlobalPrivilege

The total of four privileges is due to the sharing of that svchost instance with other services such as the Eventlog or lmhosts services.

By the way, if you wonder whether you could use svchost to host your service, the answer is you are not supposed to. Svchost is for operating system native services.

Voili voilou! That’s all folks. Our next posts will go over the other service hardening mechanisms of Vista which are the per-service SID, write-restricted services, and network restrictions.