User Account Control in Depth


No security-related feature in Windows Vista has garnered so much public and critical comment as User Account Control (UAC). UAC started life as the Limited User Account (LUA), then was renamed to User Account Protection (UAP), and finally we got UAC. The move from UAP to UAC was interesting; the “P” in UAP implies that UAP offers 100 percent bulletproof protection, which is simply untrue. UAC certainly offers a useful and viable defense and allows administrators to wield greater control and management over corporate desktops. However, local escalation of privilege vulnerabilities trump UAC, and every operating system on the planet has had, and continues to have, local escalation of privilege bugs.

Of course, it is safer to run as a standard account to help reduce the chance that malicious software can corrupt or modify the operating system (for example, installing keystroke loggers or rootkits). Remember that running as a standard user does not prevent malicious software from copying itself to locations controlled by this user, such as the user’s profile directory. Nor does running as a standard user prevent malicious software from accessing and possibly copying private data that can only be accessed by the user. Running as a standard user is very important to the health of the computer, but it is not a panacea.

Starting at the Beginning–User Tokens

When a user logs on to a Windows computer, a token is created for that user account and the token is associated with all processes started by that user. The token includes information such as:

  • The account’s security ID (SID)

  • Group membership (represented by SIDs)

  • Privileges

  • Token type

  • Impersonation level

The following code snippet shows how to access the token of the current process from C or C++:

 HANDLE hToken = NULL; BOOL f = OpenProcessToken(GetCurrentProcess(),TOKEN_READ,&hToken); if (f && hToken) {      // Cool, we have the token } else {      printf("Error getting token. Err=%d\n",GetLastError()); } if (hToken) {      CloseHandle(hToken);      hToken = NULL; }

You can get the token of another process with code such as this as long as you know the process identity (the PID) and have the necessary rights to the process and token.

 HANDLE h = NULL, hToken = NULL; if ((h = OpenProcess(PROCESS_QUERY_INFORMATION,0,pid)) &&          OpenProcessToken(h,TOKEN_QUERY,&hToken))     // success else     // failure     if (h) CloseHandle(h);     if (hToken) CloseHandle(hToken); }

If the process is running as a different user account and you don’t have access, you will need to enable the debug privilege (SE_DEBUG_NAME) using AdjustTokenPrivileges prior to calling OpenProcess.

The following code shows how to get the current process token using .NET 1.0:

  using System.Security; using System.Security.Principal; ... try {      WindowsIdentity id = WindowsIdentity.GetCurrent();      IntPtr token = id.Token;      // Cool, we have the token } catch (SecurityException e) {      Console.WriteLine(e.Message); }

You can view some of the information in a process’s token in Windows Vista using the whoami/fo list /all command, which will produce output like this:

 USER INFORMATION --------------- User Name:  homecomputer\toby SID:        S-1-5-21-2014314949-3535451937-4269012161-1002 GROUP INFORMATION ---------------- Group Name: Everyone Type:       Well-known group SID:        S-1-1-0 Attributes: Mandatory group, Enabled by default, Enabled group Group Name: BUILTIN\Users Type:       Alias SID:        S-1-5-32-545 Attributes: Mandatory group, Enabled by default, Enabled group <snip> Group Name: Mandatory Label\Medium Mandatory Level Type:       Unknown SID type SID:        S-1-16-8192 Attributes: Mandatory group, Enabled by default, Enabled group PRIVILEGES INFORMATION --------------------- Privilege Name: SeShutdownPrivilege Description:    Shut down the system State:          Disabled Privilege Name: SeChangeNotifyPrivilege Description:    Bypass traverse checking State:          Enabled Privilege Name: SeUndockPrivilege Description:    Remove computer from docking station State:          Disabled Privilege Name: SeIncreaseWorkingSetPrivilege Description:    Increase a process working set State:          Disabled Privilege Name: SeTimeZonePrivilege Description:    Change the time zone State:           Disabled

The token is for a normal user account, named Toby, and as you can see, the user’s token is not bristling with dangerous privileges.

You can use a tool such as Process Explorer to spelunk the token associated with a running process. The screen shot on the next page is an example from a process running as Toby.

More Info  

The Windows Vista development team adopted personas to represent the core customers for the operating system. The three most well known are two consumers and one IT professional. The two consumers are Abby Salazar, a mother in the home, and Toby Salazar, Abby’s son. The IT professional persona is Ichiro. The personas are concrete representations of target customers and help guide the development team when making hard design and feature decisions.

The user’s token is associated with every process the user executes, and the token is used by the operating system to make all access and privilege decisions. By default, every thread that starts within the process also gets the token, although this can be replaced using impersonation APIs such as ImpersonateNamedPipeClient or SetThreadToken.

When a protected resource (for example, a file, shared memory, or a registry key) is accessed by a running process, Windows will compare the access control list (ACL) on the object with the thread token to determine if access should be granted or not. The Windows access control model is explained in detail in Microsoft Windows Internals, 4th ed. (Russinovich 2005).

More Info  

To be more precise, a thread normally does not have a token at all–access is checked against the process token. A thread gets its own token only after it invokes one of the impersonation APIs.

Toby’s token is not special; any user logging on as a standard user in Windows XP or Windows Server 2003 would see a similar token (with the exception of the new privileges added to Windows Vista) and new SID type, the mandatory label (which is explained in more detail in the “Integrity Control” section later in this chapter).

image from book
Figure 2-1: A process running as a normal user, which is the default in Windows Vista.

Elevating to Administrator

When a standard user attempts to perform an administrative task (for example, allow a program through the firewall), the user is prompted to enter an administrative user account name and password. This dialog box is called the “Credential Prompt.” If the user does not have the credentials for an administrator, the application fails to start.

A Slight Variant: “Administrator with Approval Mode”

In prior versions of Windows, an admin is an admin is an admin. In Windows Vista, this is not necessarily true. In the interests of least privilege, even members of the local administrators group are standard users. Using the whoami/fo list/all command yields output like this for a user, Abby, who is a member of the local administrators group:

 USER INFORMATION --------------- User Name:  homecomputer\abby SID:        S-1-5-21-2014314949-3535451937-4269012161-1000 GROUP INFORMATION ---------------- Group Name: Everyone Type:       Well-known group SID:        S-1-1-0 Attributes: Mandatory group, Enabled by default, Enabled group Group Name: BUILTIN\Administrators Type:       Alias SID:        S-1-5-32-544 Attributes: Group used for deny only Group Name: BUILTIN\Users Type:       Alias SID:        S-1-5-32-545 Attributes: Mandatory group, Enabled by default, Enabled group <snip> Group Name: Mandatory Label\Medium Mandatory Level Type:       Unknown SID type SID:        S-1-16-8192 Attributes: Mandatory group, Enabled by default, Enabled group PRIVILEGES INFORMATION --------------------- Privilege Name:  SeShutdownPrivilege Description:     Shut down the system State:           Disabled Privilege Name:  SeChangeNotifyPrivilege Description:     Bypass traverse checking State:           Enabled Privilege Name:  SeUndockPrivilege Description:     Remove computer from docking station State:           Disabled Privilege Name:  SeIncreaseWorkingSetPrivilege Description:     Increase a process working set State:           Disabled Privilege Name:  SeTimeZonePrivilege Description:     Change the time zone State:           Disabled

Note again, this account is not bristling with privileges, and the administrator SID is set to deny.

image from book
What Are Deny SIDs?

The DACL in a user’s token can include SIDs that grant access and SIDs that deny access. We say admins are really users because, until they elevate, the admin SID in their token is set to deny access only and is not removed.

Why doesn’t Windows simply remove SIDs from a user’s token? The reason is simple. In Windows an object’s ACL can include deny Access Control Entries (ACEs). A deny ACE is an ACE that explicitly denies a user or a group access to the object. If an object’s ACL includes an ACE that allows read access for authenticated users, and if there is an ACE that explicitly denies the Finance group access to the object, and if Paige is a member of the Finance group, removing the Finance group SID from Paige’s token would allow Paige access to the object–because she’s no longer indentified as a member of the Finance group! However, if the Finance group SID stays in Paige’s token but is set to a deny-only SID, then she will be denied access to the object. Thus, SIDs are not removed from tokens but are specially marked for “deny only.” In other words, a deny-only SID is considered only with Deny ACEs, not with Allow ACEs.

The most common SIDs set to “deny only” are local administrators, backup operators, and network operators.

image from book

The Updated Windows Vista Token Format

There are some big differences between a Window Vista token and the token format on prior versions of the operating system. When a user logs on to, say, Windows XP, the user has just one token. In Windows Vista it is possible to have a linked token, which is also referred to as a filtered token. Essentially, if the user is a member of the local Administrators group, two tokens are created during logon: The first is the user’s standard-user-level token stripped of potentially harmful privileges with the Administrators’ SID set to deny only. The second token is the user’s full administrative token; this is often called the “elevated token” or the “full token.” Most computer operations use the first, less-privileged token. The full token is used only when the user elevates.

Windows Vista adds a number of new elements to a user’s token. We look at many of these new features throughout this chapter.

Determining if a Process Is Elevated

You can determine if your process is running elevated or not by calling the GetTokenInformation function using new flags in Windows Vista.

 BOOL IsElevated() {      BOOL fRet = FALSE;      HANDLE hToken = NULL;      if (OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY,&hToken)) {           TOKEN_ELEVATION Elevation;           DWORD cbSize = sizeof TOKEN_ELEVATION;           if (GetTokenInformation(hToken,                   TokenElevation,                   &Elevation,                   sizeof(Elevation),                   &cbSize))           fRet = Elevation.TokenIsElevated;      }      if (hToken) CloseHandle(hToken);      return fRet; }

Companion Content 

The Ch02\MegaDump sample on this book’s companion Web site is a more complete token analysis tool.

Important 

Most applications don’t need to know if they run in an elevated token process. We found that, in almost all cases, the developer should just call IsUserAnAdmin..

If you want more detail about whether the token is elevated or not, you can use the following code:

 TOKEN_ELEVATION_TYPE ElevationType; cbSize = sizeof TOKEN_ELEVATION_TYPE; if (GetTokenInformation(hToken,                TokenElevationType,                &ElevationType,                sizeof(ElevationType),                &cbSize)) {   switch(ElevationType) {     case TokenElevationTypeDefault :        // do something        break;     case TokenElevationTypeFull :        // do something        break;     case TokenElevationTypeLimited :        // do something        break;  } }

Here is a list of the three token levels that describe the “relative strength” of a token.

TokenElevationTypeDefault

Tokens with the default elevation do not have a linked token. Standard users (for example, Toby) authenticate to the system and are assigned a token with a default elevation type. Their logon session is not linked to an elevated session. The built-in Administrator account (disabled by default) may also be assigned a default elevation type if the UAC policy, “Admin Approval Mode for the built-in Administrator account” is disabled.

TokenElevationTypeFull

The token has all groups and privileges enabled that are assigned to this user account. Typically, this applies to users who are members of the Administrators group, but it also applies to a few other groups that require elevation to use full privileges, such as Backup Operators. The user’s full-privilege logon session is associated with (or linked to) another logon session for the same user with administrative groups and privileges filtered.

TokenElevationTypeLimited

The user is a member of the Administrators group or has administrative privileges assigned to the user account that are filtered from the token. The limited elevation type indicates that an associated or linked logon session is available for the same user that has all administrative groups and privileges enabled.

How to Require an Application Run as an Administrator

If you build an application that absolutely must execute as an administrator, you can add details to the application manifest that will cause Windows Vista to automatically display an elevation dialog. The manifest file should include the following:

 <?xml version='1.0' encoding='UTF-8' standalone='yes'?> <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>   <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">     <security>       <requestedPrivileges>         <requestedExecutionLevel level='requireAdministrator' uiAccess='false' />       </requestedPrivileges>     </security>   </trustInfo> </assembly>

You can add a manifest file like this by following this process in Visual Studio 2005:

  1. Launch Visual Studio 2005.

  2. Open the project that requires elevation to run correctly.

  3. Create a new XML file.

  4. Add the text from the manifest to the XML file.

  5. Save the file as projectname.uac.manifest alongside your project source code, where pro-jectname is the name of your project.

  6. Right-click Source Files in Solution Explorer.

  7. Click Add and then click Existing item.

  8. Find the manifest file and click on it.

  9. Rebuild the project.

Note that when you compile the project using Visual Studio 2005, you’ll get a benign warning that looks like this:

 Elevate.uac.manifest : manifest authoring warning 81010002: Unrecognized Element "requestedPrivileges" in namespace "urn:schemas-microsoft-com:asm.v3".

The manifest tool (mt.exe) included with Visual Studio 2005 does not recognize the requestedPrivileges element. But don’t worry, the element is understood by Windows Vista.

Caution 

Windows XP SP2 has a bug when loading an image with an embedded manifest that supports requestedPrivileges. This bug can cause Windows XP to crash when the manifest is formatted in a specific manner. A bug fix is available (Microsoft 2006a).

When you run the application, you will be prompted to elevate to Administrator or prompted to enter an administrator’s credentials, depending on your user account type.

Important  

Applications marked to elevate will fail to load during operating system startup.

Notice the reference to requestedExecutionLevel in the manifest file; this option can be one of three values as noted in Table 2-1.

Table 2-1: requestedExecutionLevel Manifest Options
Open table as spreadsheet

Name

Comment

asInvoker

Runs the application as the invoking user and does not prompt for administrator credentials or confirmation. If the user is an administrator, then the application runs as an admin.

highestAvailable

Grants the application the highest privilege available to the calling user. If the user is an administrator, the user is prompted to elevate. If the user is not an administrator, then the process starts asInvoker. Use this option sparingly.

requireAdministrator

If the user is not an administrator, then the user is prompted to enter administrative credentials. If the user is an administrator, the user is prompted to confirm, and the user’s administrator privileges and SIDs are enabled by using the linked token.

The uiAccess element in the manifest should always be false, unless your application is an Accessibility application, in which case it is true because this setting allows the application to bypass User Interface (UI) protection levels to drive input to higher privilege windows on the user’s desktop.

image from book
The Mysterious Case of the Disappearing Elevation Prompt

Here’s a little experiment. Create two accounts: the first named Abby, who is a member of the local Administrators group; and the second, named Toby, is a member of the Users group.

Logon as Abby, and run the Microsoft Management Console (MMC) by executing mmc.exe. Note that you’re prompted to consent before the application can start. Now logon as Toby, and run mmc.exe. You’ll notice that you are not prompted to elevate to administrator or enter an administrator’s credentials. Why? The reason is that MMC is marked to run at highestAvailable. MMC is an interesting tool because by itself it does not need any particular privilege to run. It is just a shell and a framework that hosts other components, and some of these components might require administrator privileges and some might not–but MMC can’t know which components you want to use a priori.

Because MMC doesn’t have a way to elevate or “restart” itself, the standard user finds himself at a disadvantage. As a normal user who wants to perform administrative tasks, the user has to exit MMC and then explicitly start MMC as admin. For “mixed-mode” applications that need both administrative and standard user actions, developers should strive to mark their application as asInvoker and then provide an elevation path for the administrative actions.

image from book

image from book
User Interface Privilege Isolation

User Interface Privilege Isolation (UIPI) blocks lower-integrity processes from sending most window messages (WM_, etc.) to higher-integrity processes. For example, a lower-integrity process cannot send window messages or hook or attach to higher-priority processes. This helps mitigate “shatter attacks” (Wikipedia 2006) in which one process tries to elevate privileges by injecting code into another, higher-privileged, process using window messages. A lower-privilege process cannot:

  • Use SendMessage or PostMessage to send most messages to higher-privilege application windows. These APIs return success but silently discard the message.

  • Use various hook functions, such as SetWindowsHookEx used by journal hooks to monitor a higher privilege process. Attempting to do so will yield an access denied error.

An additional flag in a user’s token (TokenUIAccess) indicates whether the application can potentially override some UIPI restrictions. This is mainly used by digitally signed accessibility applications located in the Windows directory because accessibility applications must access all applications to convert text on the screen to speech or provide special user interface cues.

image from book

Make an Application Prompt for Credentials or Consent

Even without a manifest, you can also force an application to prompt for administrative credentials or consent using the ShellExecute function as shown here:

 wchar_t wszDir[MAX_PATH]; GetSystemDirectory(wszDir,_countof(wszDir)); wcscat_s(wszDir,_countof(wszDir),L"\\cmd.exe"); HINSTANCE h = ShellExecute(0,                            L"runas",                            wszDir,                            0,                            0,                            SW_SHOWNORMAL);

Or the ShellExecuteEx function:

 SHELLEXECUTEINFO sei; sei.cbSize = sizeof(SHELLEXECUTEINFO); sei.fMask = NULL; sei.hwnd = NULL; sei.lpVerb = L"runas"; sei.lpFile = L"cmd.exe"; sei.lpParameters = NULL; sei.lpDirectory = NULL; sei.nShow = SW_MAXIMIZE; sei.hInstApp = NULL; ShellExecuteEx(&sei);

If you are using Microsoft PowerShell, you can start an elevated application using this syntax:

 (new-object -com shell.application)    .shellexecute("cmd.exe",$null,$pwd.path,"runas",1)

Or, from the Windows Script Host (WSH), you can use this syntax:

 var cmd = "cmd.exe"; var args = ""; new ActiveXObject("Shell.Application").ShellExecute(cmd, args, "", "runas");

If you program in C# or another .NET language, you can use code like this:

 Process p = new Process(); ProcessStartInfo psi = new ProcessStartInfo(@ "cmd.exe"); psi.UseShellExecute = true; psi.Verb = "runas"; p.StartInfo = psi; p.Start();

If you want to run elevated script commands, it is highly recommended that you do so from an elevated command window.

Starting COM Components with the COM Elevation Moniker

The example code just shown illustrates how you can start a new elevated process. But what if you don’t want to launch a new process? Let’s say you only want to launch an elevated dialog box. As an example of what we mean, perform these steps:

  1. Click the Date and Time applet in the Windows Vista Taskbar.

  2. Click Change date and time settings.

  3. Click Change date and time; note the elevation shield on the button.

  4. Enter the administrator’s credentials, or click Continue to consent.

When the Date and Time Settings dialog box pops up, this is not a separate process, it’s a COM object with a dialog box for its UI. The Date and Time applet is running as a normal user, but the Date and Time Settings dialog is running as an administrator, because setting the date or time is a privileged operation, requiring the SE_SYSTEMTIME_NAME privilege. This is all performed using a new feature in Windows Vista called the COM Elevation Moniker.

The following code shows how you can use this feature:

 HRESULT CoCreateInstanceElevated(      __in_opt     HWND        hwndParent,      __in         REFCLSID    rclsid,      __in         LPCWSTR     lpwszElevationType,      __in         REFIID      riid,      __deref_out_opt PVOID    *ppv) {      wchar_t wszCLSID[64];      wchar_t wszMonikerName[128];      *ppv = NULL;      if (0 == StringFromGUID2(rclsid, wszCLSID, _countof(wszCLSID)))          return E_OUTOFMEMORY;      int err = swprintf_s(wszMonikerName,                           _countof(wszMonikerName),                           L"Elevation:%s!new:%s",                           lpwszElevationType,                           wszCLSID);      if ( -1 == err)         return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);      BIND_OPTS3 bo;      ZeroMemory(&bo, sizeof(bo));      bo.cbStruct       = sizeof(bo);      bo.hwnd           = hwndParent;      bo.dwClassContext = CLSCTX_LOCAL_SERVER;      return CoGetObject(wszMonikerName, &bo, riid, ppv); }

The first argument to CoGetObject is the moniker, which contains information that uniquely identifies a COM object. The format for an elevation moniker is:

 Elevation:Administrator!new{GUID}

or

 Elevation:Highest!new{GUID}

Administrator maps to requireAdministrator and Highest maps to highestAvailable. You can also replace !new with lclsid; the difference is !new will call IClassFactory::CreateInstance automatically but using !clsid does not.

Warning 

Both the COM client and the COM server must be configured correctly to support elevation, which rules this technology out as a way to provide application compatibility. For a full explanation of the settings and much more information, please refer to The COM Elevation Moniker (MSDN 2006a). An elevated COM object represents a security boundary and must not blindly expose administrator functionality to a client. A client may be under the control of rogue software and may use different functions and parameters than the user intended. To protect against this, the object should be driven solely through a user interface that it presents to the user. Failure to do so might allow rogue software to elevate.

Starting Elevated Managed Code Applications

On his blog (Wille 2007), Christophe Wille offers some sample code to start elevated managed code components that can be called through a normal method call or through reflection.



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