November 21, 2003

Secure Coding: Using Restricted Tokens to execute a Process

Back at the beginning of the month I gave a tip on spawning external processes securely in Windows, and recommended that developers look at using the CreateRestrictedToken() API to restrict access of a process. I have received a couple of emails on this (why don't you guys ever want to comment on my blog?) with one email asking just how to do this using a restricted token.

I am frustrated with some of my own code right now (lets just say C# isn't all that its cracked up to be at times... I spend more time P/Invoking than anything else) and figured may as well be useful to someone and answer his question. So just for you Dennis.... here is a brief tutorial on using restricted tokens.

First off, lets set some ground work here. When possible, you shouldn't HAVE to run with elevated privileges to do things on Windows. If you are willing to accept this instead of fighting it... you will go a lot further. I met a couple of Microsoft employees at DevCon that continue to fight the idea that you need to be admin... and that runas sucks (well, they got me there.. the current implementation of runas DOES suck. I think I have bitched about that a couple of times.) The point is, you shouldn't have to settle for a weaker security posture cuz its easier. Learn how to use least privilege correctly. As a developer, you might want to read my old CodeProject article on how to develop code while running with least privilege, as this can give you some examples on how to debug issues you may have.

Anyways, with an open mind and a desire to run your code more securely, lets talk about restricted tokens. Since Windows 2000 Microsoft has given you the ability to take a user token and restrict its capabilities. (Michael Howard once called it "Dumbing down a process" which is right on the money). A process (or even an individual thread for that matter) that is running in the security context of a restricted token will be restricted in its ability to perform privileged operations, or access securable objects within the system. In this way, you can limit just what an application can do, even when launched in an elevated environment. Or better yet, lets reverse that.... you can launch in a lower security context and promote particular sections of code to run with elevated privileges (although restricted in its focus to only do what it needs to.. the whole point of least privilege). In this way, you reduce the attack surface of the application by limiting the context in which code can run.

As an example, if your application only ever needs to READ a particular key from a registry hive that requires admin privs.... why not give you just enough privs to do so... you don't NEED to write to it. (Lets ignore the fact you can apply an ACL to this particual key to give you this particular access for a moment). This is exactly what restricted tokens were designed to do.

So lets quickly look at the prototype of CreateRestrictedToken():

BOOL CreateRestrictedToken(
HANDLE ExistingTokenHandle, // Handle to Token. Usually easy to get with OpenProcessToken()
DWORD Flags, // Flags. We want DISABLE_MAX_PRIVILEGE
DWORD DisableSidCount, // Number of SIDs to disable
PSID_AND_ATTRIBUTES SidsToDisable, // The list of SIDS
DWORD DeletePrivilegeCount, // Number of privileges to disable
PLUID_AND_ATTRIBUTES PrivilegesToDelete, // The privileges we are going to disable
DWORD RestrictedSidCount, // Number of SIDs to restrict
PSID_AND_ATTRIBUTES SidsToRestrict, // SIDs to restrict
PHANDLE NewTokenHandle // Pointer to a HANDLE that we will get back
);

If you are asking yourself "what the heck is a SID", its a Security ID, which is used to uniquely identify users or groups. More technically, its really a variable length structure holding specific information about a user or a group.

In many instances, many of the fields can be set to 0 (more specifically, the 3rd through 8th field can be) if you don't care to use them. Each implementation will be different, based on what you are trying to accomplish.

Instead of blabbing any more, why don't I show you a brain dead example of how you would create a restricted token and create a new process using that (since that what Dennis asked for anyways):

HANDLE hProcessToken;
HANDLE hRestrictedToken;
// First step is to open the current process and grab its primary token
if( !OpenProcessToken( GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, &hProcessToken ) )
{
// Some witty error handling. GetLastError() holds why it failed
}
// Second step is to create the restricted token
if( !CreateRestrictedToken(hProcessToken, DISABLE_MAX_PRIVILEGE, 0, 0, 0, 0, 0, 0, &hRestrictedToken ) )
{
// Again, some witty error handling. GetLastError() holds why it failed
}
// Third step is to execute a new process with this restricted token.
// I am using fake vars for demo here... read up on this API call to see what
// they are for.
if( !CreateProcessAsUser( hRestrictedToken, path_to_exe, cmd_line_args, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupinfo, &processinfo ) )
{
// We are so witty, so witty, la la la. GetLastError() blah blah blah
}
// Last step, clean up your mess!
CloseHandle( hRestrictedToken );
CloseHandle( hProcessToken );

Thats it! When CreateProcessAsUser() executes, it will execute with a lower set of privileges than the parent process. To be useful, you would probably use the AllocateAndInitializeSid() function and modify the SID to use with CreateRestrictedToken(), (ie: Create a deny-only SID for the local account) but I don't want to do ALL the work for ya.

Hope this was helpful. Its just not that hard. Now go try it!

Posted by SilverStr at November 21, 2003 12:41 PM | TrackBack
Comments

Some strange feeling seized me when I read your comment, guys.
Does guys's post look strange here?
No. So guys, what is the point in your comment?
There always has to be some point.
Nothing personal tho.
regards,
Anderson

Posted by: Anderson.J. at February 2, 2004 01:01 PM