Security Descriptors


Securable objects have granular access controls applied through use of their security descriptors. A security descriptor is a structure that defines the following components:

  • Owner SID Lists the owning user or group.

  • Group SID Lists the owning group (primarily unused in Win32).

  • Discretionary access control list (DACL) Lists account SIDs and their access permissions.

  • Security access control list (SACL) Lists the groups and accesses that trigger an audit event.

From a code-auditing perspective, you need to look at object creation and access carefully. Chapter 2, "Design Review," discussed how an application design includes a security model to protect access to resources from potentially malicious entities. In this chapter, you can see how the object interface and access control structure implements the Windows security model.

Auditing ACLs involves examining a list of access control entries (ACEs) stored in an ACL to figure out the exact permissions associated with a resource, which includes the object's immediate permissions and any inherited permissions. An ACE is a structure that describes what type of access can be granted or denied to an entity that can be represented by a SID, such as a user or group. You can find an excellent summary on ACEs, ACLs, and their use in Secure Programming by Michael Howard and David Leblanc (Microsoft Press, 2002). As Howard and Leblanc point out, ACEs are primarily composed of a SID and an access mask describing what the entry allows or denies access to. Each ACE also has a type field in the ACE header, which describes what type of ACE it is. There are a number of different types of ACEs, but for now you just need to be aware of two main types: allow ACEs and deny ACEs. As their names imply, an allow ACE grants permission to a user requesting access to an object if the ACE SID matches the user's SID and the requested access rights are present in the ACE's access mask. A deny ACE denies a user requesting access to an object if the SID entry matches the user's SID.

Note

Writing Secure Code by Michael Howard and David LeBlanc (Microsoft Press, 2002) is generally accepted as the definitive book on secure Windows programming. This book focuses on exploring specific vulnerabilities in depth, but their book is an exceptional reference for secure coding in Windows.


Access Masks

The access restrictions or allowances an ACE imposes are identified by the mask field in the ACE structure. This field is a bit field that programmers can use to describe what type of permissions the requesting SID must have for this ACE to be relevant. The ACCESS_MASK field is divided into three categories, described in the following sections.

Standard Access Rights

Standard rights are those that can be applied to any sort of object. They govern what kind of access users have to pieces of object control information, rather than the object data itself. Eight bits are reserved to represent standard rights that can be applied to an object, but currently only five are defined:

  • DELETE Specifies deletion access for the SID in question.

  • READ_CONTROL Specifies that access can be gained for reading security information specific to the object (that is, if this flag is set and the ACE is an allow ACE, the specified SID can find out the owner and group of the object as well as read the DACL of the object).

  • WRITE_DAC Specifies the capability to write to the object's DACL.

  • WRITE_OWNER Specifies that the owner of the object can be written to (that is, a new owner can be set).

  • SYNCHRONIZE Specifies whether synchronization objects can be used on the object.

Specific Access Rights

The interpretation of bits in the specific access rights portion of an ACCESS_MASK (bits 0 to 15) depends on the type of the object in question. Specific access rights are addressed in the following sections as necessary.

Generic Access Rights

Generic access rights, described in the following list, are simple permissions that apply to all objects in some manner. There are four generic rights:

  • GENERIC_ALL Setting this right specifies unrestricted access to the object in question. It's the same as combining GENERIC_READ, GENERIC_WRITE, and GENERIC_EXECUTE.

  • GENERIC_READ Specifies read access to the object.

  • GENERIC_WRITE Specifies write access to the object so that it can be modified.

  • GENERIC_EXECUTE Specifies that the object can be executed. This right is relevant to thread, process, and file objects.

Generic access rights are translated into a combination of specific access rights and standard access rights on the object; therefore, using generic access rights require developers (and auditors) to be familiar with exactly how these flags are translated. The translation for these access rights depends on the type of object the right is applied to, and they are described on a case-by-case basis in the MSDN and throughout the remainder of this chapter.

ACL Inheritance

Objects in Windows can be containers for other objects; the most obvious examples are directories and registry keys. For this reason, Windows allows you to define separate permissions that are applied to child objects. Table 11-6 lists flags from the MSDN that describe how ACEs are applied to an object and its children.

Table 11-6. ACE Flags

Value

Meaning

CONTAINER_INHERIT_ACE

The ACE is inherited by container objects.

INHERIT_ONLY_ACE

The ACE doesn't apply to the object to which the ACL is assigned, but it can be inherited by child objects.

INHERITED_ACE

Indicates an inherited ACE. This flag allows operations that change the security on a hierarchy of objects to modify inherited ACEs but doesn't change ACEs that were applied directly to the object.

NO_PROPAGATE_INHERIT_ACE

The OBJECT_INHERIT_ACE and CONTAINER_INHERIT_ACE bits aren't propagated to an inherited ACE.

OBJECT_INHERIT_ACE

The ACE is inherited by noncontainer objects.


As these flags demonstrate, ACE inheritance can get complicated. Chapter 2 described a privilege escalation vulnerability that results from misunderstanding ACL inheritance. This vulnerability occurs because inherited permissions on the root directory make a child directory writeable to all users. In this case, it allows an attacker to write a file in a sensitive location that can later be loaded and run.

Security Descriptors Programming Interfaces

To audit object permissions, you need to be familiar with how access rights are assigned programmatically. There are several ways in which ACEs are assigned to an object's DACL. The following sections describe some of the most popular methods.

Low-Level ACL Control

Microsoft defines several "low-level" ACL and ACE control functions in the MSDN, which allow manipulating ACLs and ACEs. They also provide the capability to add ACEs to an ACL without developers being required manually create an ACE. Some of these functions are described in the following paragraphs.

The AddAce() function can be used to add a number of ACEs to the ACL specified by pAcl:

BOOL AddAce(PACL pAcl, DWORD dwAceRevision,              DWORD dwStartingAceIndex, LPVOID pAceList,              DWORD nAceListLength)


The ACE structures are supplied as the pAceList argument, which is an array of ACE structures of length nAceListLength. The dwStartingAceIndex contains an index indicating where the specified ACEs should be entered in the list of existing ACE entries. Order of ACEs is quite important and is discussed in more depth in "Auditing ACL Permissions."

The following function creates an allow ACE at the end of the ACL specified by pAcl:

BOOL AddAccessAllowedAce(PACL pAcl, DWORD dwRevision,                             DWORD AccessMask, PSID pSid)


The AccessMask and pSid arguments describe the access this ACE allows to the object in question and who this access applies to. There's also an AddAccessAllowedAceEx() function that allows the caller to specify the inheritance flags.

The following function acts in the same way as AddAccessAllowedAce(), except it adds a deny ACE rather than an allow ACE to the ACL specified by pAcl:

BOOL AddAccessDeniedAce(PACL pAcl, DWORD dwRevision,                           DWORD AccessMask, PSID pSid)


There's also an AddAccessDeniedAceEx() function that allows the caller to specify whether the ACE being added is inheritable.

The following function retrieves an ACE from the ACL specified by pAcl:

BOOL GetAce(PACL pAcl, DWORD dwAceIndex, LPVOID *pAce)


The ACE returned is the one located at dwAceIndex in the list of ACEs in the ACL.

Security Descriptor Strings

The low-level security API is a bit cumbersome and unwieldy for most permission-management tasks, so Microsoft provides an alternate text-based interface for managing security descriptors. This capability is provided by the ConvertSecurityDescriptorToStringSecurityDescriptor() and ConvertStringSecurityDescriptorToSecurityDescriptor() functions. The MSDN describes the use of these functions in detail; however, the string format accepted by these functions is briefly summarized in the following text, which lists the four types of entries in a security descriptor string:

O:owner_sid G:group_sid D:dacl_flags(string_ace1)(string_ace2)... (string_acen) S:sacl_flags(string_ace1)(string_ace2)... (string_acen)


Owner and group SIDs are fairly straightforward, but the ACE string components of an ACL require a little more explanation. The MSDN describes the format of ACE strings as shown in the following line:

ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid


The values for these fields are summarized in the following list:

  • ace_type This field specifies what type of ACE is being defined. As previously stated, the most common ones are allow ACEs, specified with an A, and deny ACEs, specified with a D.

  • ace_flags Flags can be set in this field to indicate the ACE's properties, including how and whether it should be inherited and whether it should be audited when encountered.

  • rights This field is the most important part; it includes permissions for the object being described. The generic fields are specified by using G followed by R (for GENERIC_READ), W (for GENERIC_WRITE), X (for GENERIC_EXECUTE), or A (for GENERIC_ALL_ACCESS). The standard rights are RC (for READ_CONTROL), SD (for DELETE), WD (for WRITE_DAC), and WO (for WRITE_OWNER). Finally, specific object access rights have specific encodings.

  • object_guid This field is for an object-specific ACE.

  • inherit_object_guid This field is also for an object-specific ACE.

  • account_sid This field is the SID the ACE applies to.

Putting all these fields together, here's an example of what an ACE string might look like:

A;;GR,GW;;;


Auditing ACL Permissions

Now that you're aware of the basic permissions and access rights for a generic object type, you can look into some problems associated with neglecting to set appropriate permissions for objects. As stated previously, the primary resources an application uses should have been established during the design phase. These resources are typically represented as objects in an application. A review of an application's high-level design should already have uncovered what permissions a resource requires, so now it's time to verify that those permissions have been enforced. In addition, you'll probably find objects used in applications that weren't relevant during the design phase; instead, these objects, such as the Mutex object used for synchronization, are an implementation detail. Because these objects aren't relevant during a high-level design analysis, it's likely a security policy hasn't been set and the developer might have arbitrarily chosen permissions for the object, which you need to pay attention to when auditing.

No Permissions

It's possible for an object to have a NULL DACLthat is, it doesn't have a DACL. In this case, anyone can access the object with any permission. A program that creates objects with NULL DACLs is exposing that object to interference by rogue applications that might abuse it, which can lead to exposure of information, privilege escalation, or unexpected object states and, therefore, unexpected program behavior. A NULL DACL is rarely correct, even for objects that should be accessible to everyone because a NULL DACL allows arbitrary users to change the object's owner or ACLs at any time, thus denying others access to it or exploiting some assumptions the developer made about the object.

There's a subtle nuance in how an object's DACL works. DACLs are restrictive by defaultthat is, when a DACL exists, it implicitly denies everyone access unless an allow ACE grants a user access to the object. Therefore, an empty DACL and NULL DACL are quite different. An empty DACL allows no one to have access to an object; a NULL (nonexistent) DACL allows everyone access to the object. Empty DACLs aren't important for auditing, except to mention they can be used to create object instances that are accessible only to the process that instantiated them. This capability can be used to enhance an object's security, although it's rarely used.

Applying a DACL at object creation is also not completely intuitive. Object creation functions expect a pointer to a SECURITY_ATTRIBUTES structure containing the security descriptor. However, supplying a NULL value doesn't prevent the security descriptor from being applied. Instead, the security descriptor is generated based on the inheritance properties of the container DACL, and the default security descriptor of the current token.

ACE Order

An ACL is an ordered list of ACEs, and the order in which these ACEs appear can be quite important. Higher-level APIs and GUI interfaces perform ordering on their own; however, the low-level API requires the programmer to order ACEs correctly. A developer familiar with the high-level interfaces might not understand how to use the low-level functions, which could result in a failure to apply deny entries correctly in the DACL.

Proper ordering for an ACE requires placing all deny entries before any allow entries. To understand why this order is important, review how a DACL is evaluated. Before you proceed, however, remember that access rights are evaluated only when the object handle is opened, not when an existing handle is used. This is why object creation functions accept all access rights for the object handle's lifespan.

DACL evaluation proceeds as follows:

  1. The current ACE is compared against the token's group list, and the access mask is retained if the SID is in the group list.

  2. Access is denied if the matching ACE is a deny entry.

  3. Access is allowed if the collection of matching ACEs contains all bits in the requested access mask.

  4. The process is repeated on the next ACE if access is neither denied nor allowed.

  5. Access is denied if the end of the list is reached and the collection of matching ACEs doesn't contain all bits in the access mask.

This process shows that an early allow entry could prevent a later deny entry from being evaluated. For example, a DACL in which the first ACE allows all access and the second ACE denies it would grant access on the first iteration through the list and never encounter the explicit deny entry.




The Art of Software Security Assessment. Identifying and Preventing Software Vulnerabilities
The Art of Software Security Assessment: Identifying and Preventing Software Vulnerabilities
ISBN: 0321444426
EAN: 2147483647
Year: 2004
Pages: 194

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