Saturday 28 August 2004

Run Elevated - part 1

[Let's see if I finish this series...]

Last week I wrote about running with reduced privileges. In that post I talked about using the LsaLogonUser API to create a new token, adding additional SIDs, and writing a service that would do this. In this post, I'm going to talk about some design aspects.

Let's summarize the requirements:

  • We want an API for creating a process with additional SIDs;
  • The user should, if permitted, be able to perform this operation by supplying a single password at most;
  • Administrators will be able to allow use of this service in two ways:
    • Define somehow a set of users who can always use this service - perhaps different sets for upgrading to Administrators access and for upgrading to Power Users;
    • Allow anyone by also supplying credentials to override the above - typically this will be any member of the Administrators group, but we should make it configurable;
  • This API will use some form of remote call into a service due to the privilege requirements of LsaLogonUser;
  • The created token must have the ability to interact with the caller's login session;
  • The created process must appear on the appropriate user's desktop (incl. Terminal Services sessions).

Let's look at how we do this. For authorization, we should leverage the existing security APIs as far as possible - this allows us to use the standard security dialogs which administrators are already familiar with. Therefore, we'll use the AccessCheck API to check ACLs that we store somewhere in order to check whether the user's authorised, or whether the override credentials they present (option 2 above) are authorised to override.

For consistency, even though we'll have the user's alleged password, we'll have the service impersonate its client to do access checks. We'll therefore be using their existing token, not any changes that have been made to the token since they logged on.

The right place to store the ACLs is probably somewhere in the registry; I'm going to store them in the service's Parameters key. Let's choose a name for the service: I'll call it RunElSvc. This makes our Parameters key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RunElService\Parameters. We'll put an ACL on this key itself so Administrators and LocalSystem have Full Control, and no one else has any permissions. Note that this is exactly what the SCM does - the Security key under the service contains a security descriptor (in binary format) and the key itself has an ACL as above.

I'm not sure whether it will be possible for a domain administrator to control this through Group Policy - something to look into later!

We'll also need to define the security mask bits for the ACL - the specific bits and the mapping of the generic bits. Ironically here the standard bits (DELETE, READ_CONTROL, WRITE_DAC, WRITE_OWNER and SYNCHRONIZE) don't mean a lot for this 'object'. So I think we just define bit 0 to mean 'can' if set and 'can't' if not, and map GENERIC_EXECUTE and GENERIC_ALL to set bit 0 if they're set. Actually, we could use a single security descriptor with multiple bits (one each for 'can raise to Administrators', 'can raise to PU', 'can override other bits') rather than multiple SDs.

We want the actual API to seem familiar, so starting from CreateProcessWithLogonW is probably a good idea.

BOOL CreatePrivilegedProcessW(
  LPCWSTR lpPassword,
  LPCWSTR lpOverrideUsername,
  LPCWSTR lpOverrideDomain,
  LPCWSTR lpOverridePassword,
  LPCWSTR lpApplicationName,
  LPWSTR lpCommandLine,
  DWORD dwCreationFlags,
  LPVOID lpEnvironment,
  LPCWSTR lpCurrentDirectory,
  LPSTARTUPINFOW lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInfo
);

Internally the service will call CreateProcessAsUser once it's got a token. We'll have to do a bit of copying to ensure the right environment, etc., is used rather than the service's environment.

This wouldn't be complete if we didn't think about what possible threats there will be. The major one is probably remote exploitation, which we'll mitigate by using the ncalrpc protocol. We do have to watch for the possibility that some other protocol is used, if our service ends up sharing a process, since RPC allows the union of all protocols registered by all endpoints in the process. My initial plan is for the service to run in its own process, but eventually it might run in a svchost (thanks for the comment, Larry!)

I'll have to admit I don't have experience of threat modelling.

No comments: