Chapter 13. Finding Weak Permissions

When you were a child, you probably had to ask for permission to go outside, watch television, play a video game, and so forth. If you weren’t granted permission, you couldn’t do that activity. OK, so that might not always have been true, but you get the point. In application security, permissions limit who can access certain resources and what can be done to them. For instance, a file can have permissions set to allow Tom to read it and write to it, while everyone else is denied access.

In this chapter, we discuss how to find weak permissions on objects and how malicious users can take advantage of such security bugs to attack. Even though this chapter focuses mainly on systems that run the Microsoft Windows operating system, the concepts apply to other operating systems. Also, the Windows operating system is capable of protecting resources, such as files and registry keys, but applications might define more specific permissions for objects that cannot be protected by the operating system. An example is a peer-to-peer file sharing application: the application might use its own permissions to restrict who can connect to the client and which files it can share. In such cases, you should still look for weak permissions that attackers can take advantage of to compromise the security of the application.

Understanding the Importance of Permissions

To make sure a product is secure, you have to protect its resources and the operations it performs. Although it might seem like the concept of permissions is simple, permissions are a frequent source of security vulnerabilities—it takes only a single mistake to enable your system to be compromised.

Setting incorrect permissions can lead to all types of attacks. Sometimes it is possible for an application to lead to an elevation of privilege (EoP) in which attackers are able to gain a higher level of access to the system than they should be allowed. For example, imagine an application that is running as a high-privilege user has a bug that allows arbitrary code to run. Attackers can take advantage of the flaw to elevate their privileges on the machine when executing malicious code.

Beware of multiple-stage elevation of privilege attacks. Often, when you are looking for permission problems in an application, you might not find a single bug that leads to a direct EoP to a high-privilege account. Instead, you might notice a slight EoP that seems harmless by itself. Do not be fooled by this falsity. An attacker can use this weakness to “chain” together multiple EoP attacks to achieve a higher level of privilege.

In the example shown in Figure 13-1, a piece of software allows Everyone to read and write to a registry key. This registry key specifies the path to a dynamic-link library (DLL) that was loaded by a service the software installed. The service runs as Local Service (a low privileged account), which loads the DLL. Because an attacker can control the registry key of the DLL path, the attacker can control the code that it runs (by pointing the registry key to an attacker-supplied DLL that contains malicious code). Normally, the service would then write properly formatted data to a directory that only Local Service and System (a high privileged account) are granted permission to access. Another process running as System could then parse any log files found in the directory. Because the attacker can run any code inside the malicious DLL, that attacker can write malformed files to the log directory. If there was buffer overflow in the parser for the log files, the attacker could chain the multiple stages together to run arbitrary code as the System account.

Although the developer might not have given much thought to how flaws in the parsing logic would affect the system because of the restrictive permission set, using this chaining technique could lead to an EoP attack. If you find a weakness in your permissions, do not make assumptions as to whether or not an attacker could take advantage of the weakness. The bug should be fixed so there is no question as to whether it allows an attack.

Important

It is an important best practice always to run an application or Windows Service using an account with the least privilege, as well as to secure the permissions as much as possible on objects. Doing so helps protect against certain vulnerabilities by limiting what an attacker could access. For example, if there is a buffer overrun an attacker could exploit in a service running as Local System, the attacker would have Full Control of the system. However, if the service was running as an account with a lower privilege, the attacker would be limited to whatever the account’s permissions are. Several viruses have spread because people tend to log on to the Windows operating system and run their programs with an administrator account. Microsoft Windows Vista helps with this problem by introducing the User Account Control (UAC) to distinguish between administrators and regular users. You can read more about the security improvements in Windows Vista by visiting http://www.microsoft.com/technet/windowsvista/evaluate/feat/secfeat.mspx.

An example of a multiple-stage attack that elevates privilege to the System account

Figure 13-1. An example of a multiple-stage attack that elevates privilege to the System account

Finding Permissions Problems

Although different operating systems and applications might protect their resources differently, the process of how to test for permissions problems is essentially the same. Following are the general steps to finding problems with weak permissions:

  1. Identify all of the objects, such as files, registry keys, or handles, that your application installs or uses when it runs.

  2. For each object, inspect the permissions that are applied.

  3. Using the techniques discussed later in this chapter, determine whether the permissions grant too much access.

  4. Look for places where an application creates an object with weak permissions first, and then later applies stronger permissions. These places can lead to race conditions that a malicious user can exploit.

  5. If your application installs a service, be sure that it is running using an account with the least amount of privileges necessary.

In the Windows operating system, certain resources need to be protected. The operating system enables permissions to be managed through the use of access control lists (ACLs) that specify who has access to certain resources. To make sure the ACLs are correctly protecting your resources, do the following:

  • Understand the Windows access control mechanism.

  • Find and analyze the permissions on the objects.

  • Recognize common permissions problems.

  • Determine how an attacker can access the object.

Understanding the Windows Access Control Mechanism

This section covers the high-level details of how the Windows operating system handles access control, including defining securable objects, security descriptors, ACLs, and access control entries.

More Info

For more information about how Windows access control works, refer to http://msdn.microsoft.com/library/en-us/secauthz/security/access_control.asp.

To determine the permission of a resource the Windows operating system needs to know the access level of the logged-on user and the permissions on the securable object. When a user logs on, the system creates an access token that contains the security identifier (SID) that identifies the user’s account and all of the groups the user belongs to. The system uses the access token whenever it needs to determine the permissions for a securable object or perform a certain function that requires privileges.

What Is a Securable Object?

Objects are assets on a computer that a user can use. These objects can be used either directly, such as when a user attempts to open a file, or indirectly, such as when an application reads a registry key upon starting. If the system is able to limit the permissions or control on such objects, they are called securable objects. Examples of securable objects include the following:

  • Files and directories

  • Network shares

  • Named pipes

  • Registry keys

  • Processes

  • Threads

  • Windows services

  • Mutexes

  • Semaphores

  • Shared sections

  • Active Directory objects

  • Distributed Component Object Model (DCOM) objects

Securable objects have an associated security descriptor (SD), which is defined when the object is created.

What Is a Security Descriptor?

Security descriptors contain the security information for the securable object. Essentially, the security descriptor defines which permissions are granted for that object and can include the following information:

  • The owner and primary group of the object

  • The ACLs for the object

What Is an ACL?

A security descriptor can contain two types of ACLs:

  • Discretionary ACL (DACL) that determines the level of access to a securable object

  • System ACL (SACL) that specifies the audit policy for the securable object

Both can exist in a security descriptor, but it is the DACL that secures the resource from being accessed by unauthorized users, as shown in the following graphic:

What Is an ACL?

An ACL contains zero or more access control entries (ACEs). Although many operating systems have their own method of controlling permissions, the Windows operating system uses ACLs to determine the permissions a user or group has on an object.

Important

Microsoft Windows 95, Windows 98, Windows Millennium Edition (Me), Windows CE, and Windows Mobile do not support the notion of ACLs.

If the DACL of a security descriptor is set to NULL, referred to as a NULL DACL, no access control exists on the securable object—meaning the object is not secure. NULL DACLs are discussed later in the chapter, but an object should never have one.

What Is an ACE?

An ACE describes who can do what. All ACEs contain the following information:

  • A security identifier (SID)

  • Access rights

  • Flag for the type of ACE

  • Flags that determine whether the ACE is inherited

As mentioned earlier, a SID is a security identifier that represents a user, group, or computer. The Windows operating system uses several well-known SIDs to represent different accounts (http://msdn.microsoft.com/library/en-us/secauthz/security/well_known_sids.asp). Because of the way a SID is constructed, no two SIDs are equal. Even if you delete a user named Bob and create a new user named Bob, the new account will have a different SID. Can you think of how this behavior can cause problems in an application? If an application relies on just the user name to make security decisions, it might be possible for the user name to be reassigned. As such, the new user with the reused user name will have permissions he or she should not have.

The access rights determine which operations can be performed on an object. For example, reading and writing to a file can be represented by the FILE_READ_DATA and FILE_WRITE_ DATA flags, respectively. Full Control can also be granted in an ACE, meaning that an account can do anything to the resource, such as read, write, delete, or even change the permissions. Not only can an ACL indicate which rights are allowed, an ACE can also deny access to a resource.

When creating an ACE, a flag can be set to determine how the system will propagate inheritable ACEs to child objects. Depending on the combination of inheritance flags, an ACE can be inherited by the container only, by the objects only, by both the container and objects, or by neither. For example, a directory can be considered a container and a file can be considered an object. If the directory has the CONTAINER_INHERIT_ACE and OBJECT_INHERIT_ACE flags set for an ACE, all child directories and files created under the parent directory will also inherit the ACE.

Finding and Analyzing Permissions on Objects

One approach to testing permissions might be to log on with a user account that does not have permissions to the resource and verify whether access is denied. Although this method might seem reasonable, it is flawed and will cause you to miss permission problems. For example, say a product restricts a registry key so that only administrators have access to it. That registry key has a subkey that grants permission to the Everyone group. The bug might be dismissed because using the registry editor to attempt to navigate to the subkey as a nonadministrator causes an access denied error. Just because the user gets an access denied error when using the registry editor does not mean that enough testing has been completed to make sure that user really does not have access. The registry key that granted Everyone permission could be accessed if the exact path was used to specify the key, which can easily be done with just a little bit of code.

Before you can determine whether there are any permissions problems, you need to determine the permissions on your resources. Table 13-1 lists three tools you can use to make it easier to find permissions problems on resources.

Table 13-1. Common Tools Used to Find Permissions on Objects

 

AccessEnum

Process Explorer

ObjSD

Files/directories

X

X

X

Network shares

X

 

X

Named pipes

 

X

X

Registry keys

X

X

X

Processes

 

X

X

Threads

 

X

 

Windows services

  

X

Mutexes/semaphores

 

X

X

Active Directory objects

  

X

You can use other tools to find permissions problems in addition to those listed in the table; however, this section discusses how to use the listed tools. The next section focuses on the details of finding common permissions problems.

Using the Windows Security Properties Dialog Box

In the Windows operating system, you can view the permissions on files, directories, or registry keys. Figure 13-2 shows an example of viewing the permissions on a registry key by right-clicking the key in Registry Editor and selecting Permissions. You can view the security properties on files and directories in a similar dialog box.

Permissions dialog box for a registry key

Figure 13-2. Permissions dialog box for a registry key

The Permissions dialog box shows that only the Administrators group and the System Account have permissions on the registry key. To see an advanced view of the permissions, as shown in Figure 13-3, click the Advanced button.

Advanced security settings for a registry key

Figure 13-3. Advanced security settings for a registry key

To see the exact permissions select the permission entry and click Edit. This will show you the exact permissions on that object for that user or group, as shown in Figure 13-4. Notice in the figure that the Administrators group has only the Write DAC and Read Control permissions on this registry key.

Permissions entry for registry key

Figure 13-4. Permissions entry for registry key

Although you can view the permissions for files, directories, and registry keys by using the security properties, you cannot solely rely on that information. Instead, you have to use other tools, especially if you want to check permissions problems with your application on objects other than files, directories, or registry keys.

Using AccessEnum

AccessEnum by Sysinternals (http://www.sysinternals.com/utilities/accessenum.html) makes it easy to enumerate the permissions on files, directories, network shares, and registry keys and their children to spot potential problems. The tool has two display settings:

  • Objects that have less restrictive permissions than the parent object

  • Objects that have permissions different from the parent object

Because this tool looks at all of the permissions from a particular parent down, it makes it easier to spot when the permission levels for an object have changed. Just specify a directory, network share, or registry to display who has access. Figure 13-5 shows the results of running AccessEnum against where programs generally are installed.

Using Process Explorer

Although you might know some of the files and registry keys your application uses while running, the application probably uses a lot more than you think. Also, your application might use other securable objects that are harder to detect, such as mutexes and semaphores. Because the application might use these other types of securable objects, make sure you check the permissions on them as well.

Results of running AccessEnum on the Program Files directory

Figure 13-5. Results of running AccessEnum on the Program Files directory

Process Explorer by Sysinternals (http://www.sysinternals.com/utilities/processexplorer.html) enables you to see the securable objects that an application creates and uses, and it also displays the Security dialog box for the object. To use this tool, simply start Process Explorer and select the process that you want to observe. When you select a process, the handles the process currently has open are displayed in the lower pane of the tool, as shown in Figure 13-6. You can look at the security permissions on an object by right-clicking the entry and selecting Properties.

Viewing the handles of an application in Process Explorer

Figure 13-6. Viewing the handles of an application in Process Explorer

As you can see in Figure 13-6, which shows the handles of the application FwcAgent.exe in Process Explorer, there are several securable objects. If this were an application you were testing, you would need to check the permissions of each of the objects.

Using ObjSD

As mentioned earlier, an application can use securable objects other than files, directories, and registry keys. The command-line tool ObjSD, which you can find on this book’s companion Web site, can be used to view the details of the security descriptor for a majority of securable objects. To use this tool, simply specify the object type and object name as command-line arguments. The following is the output of ObjSD on a directory called permissionExample.

D:>objsd.exe directory permissionExample

Owner : BUILTINAdministrators
Group : SECBOOKNone

ACE[ 0] : Allow : BUILTINAdministrators : 0x1f01ff
        : ( Del RCtl WDac WOwn Sync )
        : ( )
        : ( List AddFile AddSubDir ReadEA WriteEA Traverse DeleteChild ReadAttr
WriteAttr )
        : ( ContainerInherit ObjectInherit )

ACE[ 1] : Allow : CREATOR OWNER : 0x1f01ff
        : ( Del RCtl WDac WOwn Sync )
        : ( )
        : ( List AddFile AddSubDir ReadEA WriteEA Traverse DeleteChild ReadAttr
WriteAttr )
        : ( ContainerInherit InheritOnly ObjectInherit )

ACE[ 2] : Allow : BUILTINUsers : 0x100006
        : ( Sync )
        : ( )
        : ( AddFile AddSubDir )
        : ( ContainerInherit )

The output reveals the following information about the security descriptor:

  • The creator/owner of the directory was in the Administrators group.

  • The DACL contains three ACEs.

  • The order of the ACEs is shown.

  • Each ACE shows which permissions are granted.

The first line shows the ACE type (Allow or Deny) for the specified user or group. The second, third, and fourth lines are the standard rights, generic rights, and object-specific rights, respectively. The last line shows the inheritance settings. Using the output shown, can you tell what the last ACE does? It grants all Users access to use the object for synchronization and to create directories and files below it, and it causes the children to inherit the permissions from the container.

Using AppVerifier

Microsoft has a tool called AppVerifier that can be used to test an application for several different issues. Even though the tool is primarily used to test for compatibility issues with the Windows operating system, it can also detect some common security issues, especially access control issues that an application might create. Refer to http://msdn.microsoft.com/library/en-us/dnappcom/html/AppVerifier.asp for more details on this tool.

Recognizing Common Permissions Problems

At this point, you should understand Windows access control, but how do you know if there is a problem? The short answer is, never grant permission to a resource to anyone that should not have access. Sounds like a simple concept, but there are many ways in which permissions could be granted accidentally. This section covers the most common problems with access control and how to find such issues in your application.

Weak DACLs

To determine whether a user has access to an object, consider the following:

  • If a DACL exists and there are no ACEs, no one has access.

  • If ACEs exist, each ACE must be checked for the correct access control.

  • If a DACL does not exist, all users have full access.

A weak DACL means that you granted permissions on an object that allow too much access. This problem could be caused by placing the permissions on the object itself or the container in which the permissions where inherited. Examine each ACE in the DACL to understand who has access to the object. Some things to watch out for include the following:

  • Giving Everyone access

  • Giving large groups access

  • Giving too much access to the container

  • Using a deny ACE

Giving Everyone Access

There is a group in the Windows operating system called Everyone. If you see this group used, it should be a red flag. As such, any permission granted to the Everyone group is bad. For instance, if there is a Write ACE for Everyone, anyone can write to your object. This vulnerability can lead to several types of attacks.

Note

In Microsoft Windows XP Professional Service Pack 2 (SP2) and Windows Server 2003, the Anonymous Logon group no longer is a member of the Everyone group. In previous versions of the Windows operating system, the Anonymous Logon group had access to many resources because it was a member of the Everyone group.

You should especially look for the following permissions and make sure that they are never granted to the broad groups, such as Everyone and Authenticated Users, unless in situations your application absolutely requires them:

  • WRITE_DAC. Allows the DACL to be modified

  • WRITE_OWNER. Allows the owner to be changed

  • FILE_ADD_FILE. Allows files to be created, including malicious executables

  • DELETE. Allows the object to be deleted

  • FILE_DELETE_CHILD. Allows any child file or folder object to be deleted, even if the user doesn’t have access to the child object

Note

The WRITE_OWNER permission allows a user to become the owner of an object. If you are the owner of an object, you have WRITE_DAC permission on it, which can be used to obtain all the rest of the permissions. Hence, any ACL that has the WRITE_OWNER or WRITE_DAC permission should be reviewed to make sure it was expected.

Giving Large Groups Access

Much like giving the Everyone group access to an object, you should be aware of other groups in the Windows operating system because they encompass a lot of users. Table 13-2 lists common SIDs that you should watch out for because they can give a lot of people access to your resources.

Table 13-2. Common Large Groups

Display name

SID

Reason

Everyone

S-1-1-0

Includes all users, even anonymous users and guests in some versions of the Windows operating system.

Local

S-1-2-0

Obsolete group that includes all users that are logged on locally. Because it is considered obsolete, do not use it.

Network

S-1-5-2

Includes all users that have logged on through a network connection, such as by accessing a network share.

Batch

S-1-5-3

Used when scheduled tasks are executed.

Interactive

S-1-5-4

Includes all users that have logged on interactively to the active desktop.

Service

S-1-5-6

Used when all Windows Services are started, except Local System services.

Anonymous

S-1-5-7

Includes all users that have logged on anonymously or that have a null logon session.

Authenticated Users

S-1-5-11

Includes all users and groups that have been authenticated. All users in a domain and all trusted domains are part of this group.

Remote Interactive Logon

S-1-5-14

Used when a user is logged on through Terminal Services.

Users

S-1-5-32-545

All local users are automatically added to this group when a local user account is created.

Guests

S-1-5-32-546

Includes the IUSR_computername and IWAM_computername that Microsoft Internet Information Server (IIS) uses for the anonymous user.

Depending on your application, it might make sense to grant such a group access to a resource. You need to analyze the ACE using the context of your feature, though, and question whether it is appropriate access or whether the developer was being too liberal with the permissions.

Giving Too Much Access to the Container

Even if all of your objects inside a container have the correct access control placed on them, you still need to check the container object to see if it gives too much access. Examples of a container object include these:

  • Directory. Can contain child directories and files

  • Registry key. Can contain other registry keys or data

  • Process. Can launch threads

Although it is possible to restrict the permissions on the child objects, the parent objects could have ACEs that allow too much access, such as allowing tampering with a parent object, additional child objects to be created, or permissions to be modified.

The following scenario demonstrates how giving too much access to the container might cause a problem:

  • An application is installed to a location on the hard drive.

  • All of the application’s files have permissions set on them to allow access only to administrators.

  • The directory in which the application is installed allows any user to create new child objects.

  • The application does not directly use any other files in the installed directory.

Because an attacker can’t tamper with any of the application files, the application is safe, right? If you said no, you are correct. Because the container directory has weak permissions that allow creation of child objects, an attacker could potentially place malicious files in that directory and cause the application to use them to execute malicious code. This situation is possible if the application relies on specific shared DLLs to start. The attacker can use a technique known as dynamic-link library redirection to force the application to load an attacker-supplied DLL instead of the shared one. For example, if the application is called Runme.exe, the presence of a file called Runme.exe.local in the same directory will cause the Windows operating system to load DLLs from that directory first, even if the application Runme.exe specifies the full path to the DLL when calling the LoadLibrary or LoadLibraryEx API.

More Info

For more information about dynamic-link library redirection, refer to http://msdn.microsoft.com/library/en-us/dllproc/base/dynamic_link_library_redirection.asp.

Even if the application doesn’t load DLLs, it might automatically load other files from the directory that could lead to an attack. Imagine that an application has a designated folder that contains scripts. When the application loads, it iterates through all of the scripts in the particular folder and runs them. Even if the script files have permissions set to prevent an attacker from tampering with them, the folder might allow anyone to add additional script files, which will also be loaded. And the problem does not apply just to files and directories. Any time the container has weak permissions, there is a security vulnerability.

Important

Any time you give Write access to a directory that contains an executable, it is the equivalent of giving Write access to the executable itself.

Using a Deny ACE

As mentioned earlier, an ACE can either allow or deny access to a particular resource. Whereas using a deny ACE in addition can provide defense in depth, it can cause problems, too. A deny ACE is analogous to using block lists, which is generally a bad approach to use to set security restrictions.

You might wonder how denying access can be a bad thing. Like many block lists, this type of access denial is usually easy to get around. For instance, we worked on a product that denied a certain group access to a resource, but allowed all authenticated users access to the resource. To gain access, attackers simply needed to remove themselves from the group. Typically, an attacker cannot change the groups they belong to, but imagine that an organization uses a deny ACE on a group called Recruiters. If you are a recruiter, you belong to that group. However, what happens when you change jobs within the company and take on a marketing role in the organization? You would probably be removed from the Recruiters group and be added to the Marketers group. Now you aren’t denied access anymore to the resources the Recruiters group is restricted from.

Maybe this setup is appropriate for your application, but you should be suspicious if you see any deny ACEs used for an application. If you can easily get around the check, there is a bug in the application. If you want to allow access only to a certain group of people, using an allow list is preferable to using deny ACEs.

NULL DACLs

If the current DACL in a security descriptor for an object is set to NULL, it is called a NULL DACL. A resource that has a NULL DACL has no access control, or, in other words, Everyone has Full Control to the object. Do you see the problem? If all users are granted Full Control of the object, they can do anything with the object. For example, an attacker could cause a denial of service (DoS) by changing the permissions to Deny everyone access to the object. In almost all cases, there is no reason to have a NULL DACL on an object. If you want to grant all users the ability to read, write, modify, and delete data in an object, set the DACL to allow just those permissions—don’t use a NULL DACL.

To find NULL DACLs, you can use the tools mentioned earlier in the chapter, or you can search the code. Sometimes the following might be a quicker way to spot problems with NULL DACLs:

  • Search the code for the SetSecurityDescriptorDacl API being called with a NULL DACL.

  • Search the code for the SECURITY_DESCRIPTOR structure being initialized with a NULL DACL.

Improper Ordering of ACEs

In a DACL, the order of the ACEs matters. The first ACE in the list takes precedence over the remaining ACEs. As mentioned earlier in this chapter, ACEs can also be inherited. The permissions that are applied most directly on an object should take precedence, meaning permissions set on an object should take priority over the permissions inherited by its container.

When you change the permissions using the Windows Security dialog box, the ACEs will always be ordered correctly. However, the problem of ordering ACEs is when ACLs are being built in code. For example, if your application adds an ACE to an existing ACL on a resource, it needs to ensure that it maintains the proper order, which is as follows:

  • Deny ACE

  • Allow ACE

  • Inherited Deny ACE from parent

  • Inherited Allow ACE from parent

  • Inherited Deny ACE from parent’s parent (grandparent)

  • Inherited Allow ACE from parent’s parent (grandparent)

  • And so forth

It is essential to get the ACE order right; otherwise, the security permissions on the resource will be useless. Although the Windows Security Properties dialog box will display an error if the order is incorrect, as shown in Figure 13-7, it is up to you to make the order correct by using tools like ObjSD.

Windows Security warning for a directory that has incorrect ACE order

Figure 13-7. Windows Security warning for a directory that has incorrect ACE order

Object Creator

When a securable object is created, the owner always has access to the resource, even if the ACL denies access to that user. Unless the system policy is changed, whoever creates the object becomes the owner, except in Windows Server when the object is created by an administrator, the Administrators group is the owner. The only way the owner of an object can change is if one of the following happens:

  • An administrator assigns a new owner.

  • Anyone with the WRITE_OWNER permission

  • Any user or group that has the Take Ownership (SeTakeOwnershipPrivilege) privilege

  • A user who has Restore Files And Directories (SeRestorePrivilege) privilege

Can you think of how this can be a problem? One simple example is if the system uses a quota system for users, restricting the number and/or size of files they own. If you could set the owner of an object to anyone, the object wouldn’t count against your quota because you changed the owner of the file.

Also, if you create a container, you become the owner and have access to any child objects created in that container that inherit the ACL. For example, suppose an ordinary user is a member of a group. The user doesn’t do anything malicious while a member of that group. Then, the user is removed from the group. Even after time passes and the machine is rebooted several times, the user is able to compromise the system because any container objects that allow a group to create a child object are owned by the user, not the group. When the user is removed from the group, the user still has access to the objects he or she created unless the application is designed so that group is the owner, not the individual creator.

Accessing Resources Indirectly

If a burglar cannot break into a house through the front door, do you think the thief will just give up? Of course not: the burglar would try a back door, a window, the roof, and so on. Even with ACLs, sometimes a resource isn’t protected if there is more than one way to gain access to it. And some operating systems do not provide an access control mechanism to protect a resource. In these cases, you have to think of methods that might allow an attacker to bypass any protection.

Take the following example. Once we tested a Web site that provided news articles to the site’s members. When a user clicked a link to display the article, the Web application loaded the file specified in the URL query string and displayed it in the browser. Can you see the potential problem? By using the directory traversal technique discussed in Chapter 12, we were able to retrieve files outside of the directory that stored the articles. Normally, we wouldn’t have any access to those files because anonymous users aren’t granted permission to access them. However, we didn’t access the files using the anonymous user account. We were able to access the files as the account that the Web server was running as, and thus permission was granted.

When you are trying to protect a resource, you need to think of all the methods that might be able to gain access to it. In the preceding example, the Web application had a canonicalization bug that had to be fixed, but the unauthorized access could have been prevented if the account the Web server ran as was not granted access to the files. If there are multiple ways a resource can be accessed, restricting access using only one mechanism can still leave your resource vulnerable.

Forgetting to Revert Permissions

An application can change privileges and run under the context of another user in many different ways. If your application does this, you need to make sure that it properly reverts back to the proper user. For example, if you have a Web application that uses the permissions of the logged-on user, it might call RevertToSelf to perform higher-privilege operations on behalf of the user. However, if the application does not revert back, the user is now running under the context of a higher-privileged account. This flaw is introduced mainly by improper handling of error conditions. Look at the following code:

public void DoSomething()
{
    // Revert to a higher-privileged user.
    SecurityContext secCon = SecurityContext.RevertToSelf())

    // Calling this method could throw an exception.
    DoSomethingElse();

    // Revert back to original impersonated user.
    secCon.Dispose();
}

When DoSomething is called, it reverts to self, which could be a higher-privileged user. At this point, the application is running in a different security context when DoSomethingElse is called. If calling DoSomethingElse throws an exception, it isn’t being handled by the DoSomething function. As such, it might be possible for malicious code that called DoSomething to catch the exception, and then that code is running under the higher-privileged user. This vulnerability uses a technique known as exception filtering, which is covered in more depth in Chapter 15, but this example illustrates how forgetting to revert permissions can cause problems in your application.

Squatting Attacks

Permissions often are set on an object using the function that creates an object. Sometimes developers properly secure objects when the objects are created. (If they don’t, it could lead to race condition attacks, which are discussed in the next section.) If the object already exists, the creation of an object using the same name will fail. A squatting attack, also referred to as a pre-creation attack, occurs when an attacker creates an object with the same name as an object the application will create prior to the program attempting to create it. If the name of the object is predictable, the attacker will easily be able to guess the name.

Squatting attacks can occur with any named object, including files, registry keys, sockets, and named pipes. The real problem is when users have Write access to a shared namespace. To test for squatting vulnerabilities, create an object with the same name as the object the program creates and see whether you are able to maintain access to the object.

Exploiting Race Conditions

Race conditions occur when the timing of certain events influences the outcome of a program. You should consider that an attacker can perform actions between each line of code in the program you are testing. For example, if an object is created and then secured later, an attacker can take advantage of the window of opportunity provided after the object is created but before it is secured.

Timing is important when an attacker attempts to exploit a race condition, and getting the timing just right often isn’t as difficult as you might think. To increase the odds of exploiting a race condition flaw, the attacker might attack repeatedly and quickly. For example, in the case in which an object is created and then secured, an attacker could write a program that loops quickly and attempts to access the object until it succeeds. Following are some situations that could lead to a race condition issue:

  • A resource with weak ACLs intended to be temporary is created.

  • A resource is created in a container that has weak ACLs.

  • A resource is created in a container that was pre-created by a malicious user.

  • The resource itself is pre-created by the malicious user.

  • The resource is created and then the ACLs are set afterward.

  • The resource is created in one container and moved to another.

  • The permissions on the thread are momentarily elevated to accomplish a different task.

When a programmatic handle is created for a securable object, the Windows operating system determines the permission needed for the object at that time. If the permissions for a resource are changed while a handle is still open, an attacker with a previous handle to the object will continue to have access.

Another type of race condition that causes many security vulnerabilities is called a Time of Check Time of Use (TOCTOU) attack. The attack occurs when a program checks for a certain condition, and then performs an action based on that check. For example, suppose a program downloads updates in a file named Update.exe to an insecure location. To prevent tampering the program verifies the file is signed by the software’s author, and then runs Update.exe. The signature check prevents squatting and tampering attacks before the check. However, the attacker could swap Update.exe with malicious code after the signature is verified but before Update.exe is run.

Note

Sometimes a file’s permissions allow an attacker access to a temporary file. However, the application that creates the temp file might have an exclusive lock that prevents an attacker from opening the file. The lock is released only when the application is terminated, and at that point the application deletes the temp file. Hard links, which are discussed in the next section, are an easy way for an attacker to gain access to the file. Because a copy of a file’s contents is made when a hard link’s destination is deleted, an attacker can make a link to the file that is exclusively locked and then obtain a copy when the locked file is deleted from the file system.

Changing the permissions on a securable object can be extremely painful because it is difficult to find whether any open handles are using the objects. You can use the techniques mentioned earlier in this chapter to determine whether your application creates the object first and then later changes the permissions. If it does, you can almost guarantee there is a race condition bug.

Testing for race conditions can get tricky. One of the most efficient ways to test for this type of vulnerability is to use a debugger and step through the code. With a debugger, you are able to see each line of code executed and have the opportunity to perform actions of your choice outside of the debugger before the next line is executed.

File Links

Whenever a file can be represented by more than one name, interesting canonicalization security tests should be performed, which the preceding chapter covers. Both the Windows file system (NTFS) and other various file systems support the notion of linked files. A file link is much like a pointer in C. The pointer, called a link, is a dummy file that points to its target. When file operations are performed on the link file, the operation actually takes place on the destination of the link. For example, suppose a link file named c: empFileLink.txt points to c: empOriginalFile.txt. If you open FileLink.txt in Notepad and change the contents, the contents of OriginalFile.txt will actually be changed. Maybe you are already starting to see the potential security problems.

Please note that a linked file is different from a Windows shortcut file (.lnk). A shortcut file always ends with the .lnk extension and is just an ini file that references the place it is pointing to. If the shortcut file is opened in Notepad, the contents of the shortcut file is in its ini format. You will not be editing the file the shortcut points to.

There are different types of file links—symbolic links, junctions, and hard links. The following sections examine each.

Symbolic Links

Here are some important characteristics of symbolic links (also called soft links):

  • Symbolic links can link to files across file systems.

  • The destination of a symbolic link can be a file or directory.

  • All operations performed on the link file are actually performed on the file the link points to. Deleting the link removes the link file, but does not delete the destination of the link.

  • If a symbolic link’s destination is removed, the link is broken. If the destination is re-created, the link will work again.

  • Creating a symbolic link requires no permissions to the link’s destination.

  • A symbolic link can be made to a nonexistent file. If the target of a link is later created, the link will work.

Symbolic links are not fully supported in the Windows operating system; a subset of the symbolic link functionality is available and is called a junction. More information about junctions is provided in the following section.

On a UNIX-based systems (*NIX) creating a symbolic link is easy when you use the ln command.

Junctions

In the Windows operating system, symbolic links are not fully supported. However, it is possible to create a symbolic link to a directory. This link is called a junction. The following are the characteristics of a junction:

  • A junction is a symbolic link to a directory on a system that runs the Windows operating system. A junction cannot be made to a single file.

  • Links can be across file systems.

  • File operations performed on files inside the junction are actually performed on the files inside the directory the junction points to. Removing the junction directory only removes the link. The directory the junction points to is untouched.

  • If the junction’s destination directory is removed, the junction is broken. If the destination is re-created, the junction will work again.

  • Creating a junction requires no permission on the link’s destination.

  • A junction cannot be made to a nonexistent directory.

A junction can be created by using the Sysinternals Junction tool (http://www.sysinternals.com/utilities/junction.html).

Hard Links

Hard links are similar to but also different from soft links. Following are hard link characteristics:

  • The hard link and the destination of the link must exist on the same file system.

  • The destination can only be a file. (Linking to a directory is not supported.)

  • Just like other links, all operations performed on the link are actually performed on the destination of the link. Deleting the link removes the link, but does not delete the destination of the link.

  • If the destination of a hard link is removed, the link isn’t broken. The links pointing to the target will retain the contents of the file when it was deleted. If there is more than one file linking to the original destination of the link, these files will remained linked together.

  • Creating a hard link requires Read permission on *NIX and the Write Attributes (FILE_WRITE_ATTRIBUTES) permission in the Windows operating system.

  • Hard links cannot be made to nonexistent files.

Creating hard links on *NIX can be accomplished by using the ln command. Creating a hard link in the Windows operating system can be done by using Fsutil.exe, which is shipped with Windows XP and later. This utility will run, however, only if you are logged on as an administrator. The CreateHardLink API does not require the caller to be an administrator. When testing links, test as a low privileged account—not as an admin. The Ln.exe tool included on book’s companion Web site enables you to call the CreateHardLink API directly without being an administrator.

Security Concerns with File Links

File links provide some interesting cases for canonicalization testing. If a feature is attempting to block a file or directory based on its name, a link can be used to change the name used to access the file. This name change might allow for bypassing the security check.

In addition to the traditional canonicalization name differences, links provide a way to reference files or directories that exist on different parts of the disk. Programs often perform file operations on files located in specific locations. For example, suppose that when a document is being edited a word processor always creates or uses an existing temp file with a known filename. While the document is being edited, the word processor stores various temporary information in the temporary file. If the temporary file is actually a file link, the word processor might be modifying the contents of a file other than its temporary file.

For other security reasons, developers often write temporary files to locations that only the current user has access to write to. So, users who launch the word processor are only harming themselves if they precreates the temp file as a link file. However, sometimes programs run with elevated privileges that differ from the person invoking certain functionality in a program. Other times, files created by one user are used by another user.

A simple example of such a program that runs with privileges different from the user invoking its functionality is in a game called xbreaky (http://xbreaky.sourceforge.net) in all versions prior to 0.0.5. Xbreaky is installed on *NIX systems with the suid bit, which means that when the program is launched, the process runs under the privileges of the owner of the executable file, not the caller. In this case, xbreaky’s owner is the root account, so the game would run as root even though it was executed by a user account. The xbreaky program would write high scores to a file named .breakyhighscores in the home directory of the user who launched the program, which seemed like a safe place to store that information: only the person who launches the program should have access to the file. However, because the program is running as root, not as the user who launches the game, the user launching the program can maliciously abuse the permissions the program is running under. Marco van Berkum found a way to abuse xbreaky. He found that if he created a symbolic link named .breakyhighscores in his home directory to an arbitrary file, the game would overwrite it with the contents of the name of the high-score user (which he could control when using the game). He could point the symbolic link at any file on the system and the game running as root would happily overwrite its contents with the high score user information. More information about this vulnerability is available at http://lists.grok.org.uk/pipermail/full-disclosure/2002-September/001302.html.

The Windows operating system has functionality similar to suid on *NIX called impersonation that enables a program to change who the program is running under. To identify which programs impersonate a user while the program is running, use a program from Sysinternals called Tokenmon (http://www.sysinternals.com/utilities/tokenmon.html). This program lists which processes are impersonating different users and the users that are being impersonated. Programs that run with privileges that are different from the person using the program (especially programs running with elevated privileges) should be security tested rigorously.

Determining the Accessibility of Objects

The next step in determining whether your resources are vulnerable as a result of weak permissions is to determine whether they are accessible to an attacker. A lot of the resources in the Windows operating system require you to be logged on to the machine to attack them, but certain objects are accessible remotely. These resources are the ones that attackers target first because they can remotely connect to the machine and take advantage of the weak permissions.

Remotely Accessible Objects

As mentioned, these objects are the ones you should be most concerned about because they allow an attacker to access them remotely and create a harmful exploit. The securable objects that are accessible remotely are these:

  • Windows services

  • DCOM objects

  • Files

  • Registry keys

  • Named pipes

Windows Services

Anything you can do with a service locally, you can also do remotely, such as start, stop, and change the configuration of the service. Services are interesting to an attacker because they generally run as a higher-privileged account. If your application installs a service, make sure you adhere to the following rules of secure service implementation:

  • Enable the service only if it is absolutely needed. Critical services that are necessary for your application to run should be started by default. By automatically enabling a service for your application, your attack surface increases.

  • Always run the service with the least privileges. As mentioned earlier, you should not run an application with an account that has more privileges than needed. This principle also applies to services.

A service can run as a local or domain account, and three built-in system accounts in the Windows operating system allow a service to access resources and objects. The following system accounts are listed in order of permission from low to high:

  • Local Service account. The Local Service account has reduced privileges, much like the ones of an authenticated user account. Any services that run as Local Service that connect to a network resource use the anonymous credentials. The actual account name is NT AUTHORITYLocalService.

  • Network Service account. The Network Service account is like the Local Service account, but accesses network resources using the credentials of the computer account. The actual account name is NT AUTHORITYNetworkService.

  • Local System. The Local System account is the most powerful of the three built-in service accounts. It has full access to the computer’s resources, including the directory services if the service is running on a domain controller. A service running as this account has access to network resources like any other domain account. The actual account name is NT AUTHORITYSystem.

Unfortunately, because of the number of installed services that increase a system’s attack surface, even a flaw exploited in a service running as Local Service or Network Service can lead to elevation of privileges to Local System through chaining multiple security vulnerabilities. To prevent this type of attack, it is better to run your service as an account that has limited access; however, this is not always possible or desirable.

Also, if you use a domain or local account as the service identity, the credentials can be retrieved in clear text using tools that dump the LSA secrets. Generally, this situation might not be a huge concern if you own the machine or the domain it might be in. However, imagine that an organization sets up each workstation and runs backup software as a domain administrator account. In such a case, the local user with administrator rights on the workstation will be able to dump the domain administrator’s account information.

Make sure you thoroughly test your service for security vulnerabilities that could lead to compromise. Don’t ever let “It runs as Local Service, which can’t really do anything” be an excuse for not fixing a security flaw.

When dealing with services, not only should you consider the account the service runs as, but also the rights granted when accessing the service. If an application uses the OpenService, EnumServicesStatusEx, or QueryServiceLockStatus API to create a handle to a service, it can open the service with the access rights such as SERVICE_ALL_ACCESS, SERVICE_ALL_STOP, SERVICE_ALL_START, and so forth. If the application intended only to start or stop the service, there is no reason that it should grant additional access rights because doing so can lead to an EoP attack.

You can also use ObjSD to observe the ACLs set on a service. In particular, you should question any service that has the ChangeConf, WDac, or WOwn rights set on it because this could lead to an EoP attack—and a remote EoP, at that.

In fact, Microsoft Security Bulletin MS06-011 (http://www.microsoft.com/technet/security/bulletin/ms06-011.mspx), released on March 14, 2006, addresses weak ACLs in several Windows services that could lead to elevation of privilege.

For more information about Windows services in general, refer to http://msdn.microsoft.com/library/en-us/dllproc/base/services.asp.

DCOM Objects

Several applications, including Microsoft Internet Explorer and Microsoft Office Word, use DCOM, which enables the application to be remotely controlled. Although this technology allows constructing applications on a distributed computing environment, it also enables an attacker to potentially exploit software that doesn’t set proper permissions on its DCOM objects.

DCOM has a lot of security features, but very few security bugs because it has decent permissions by default. To use the DCOM object, you need permissions to the remote box and DCOM permissions to use the object. Thus, DCOM objects are exploitable really only when they are not properly configured or not tested. The Windows operating system has good defaults in DCOM objects that do not have custom permissions, so you should make sure your objects aren’t less secure than the default permissions provided by the platform. You can use tools such as Dcomcnfg.exe, which is included in the Windows operating system, to examine the permissions on your DCOM objects.

At the Black Hat Windows Security 2003 briefings, SecurityFriday (http://www.securityfriday.com) gave a good presentation that is worth reading if your application uses DCOM; the presentation can be found at http://www.blackhat.com/presentations/win-usa-03/bh-win-03-securityfriday/bh-win-03-securityfriday.ppt.

Locally Accessible Objects

Some objects are available only when a user is logged on to the system, either at the desktop or a terminal server session. Kernel objects, such as mutexes and semaphores, are all local, and an attacker must be logged on to the system to exploit a flaw. Some people believe these objects are not accessible across sessions, meaning you can’t attack another user currently logged on to the same machine. This claim is not true. You can access the named object by specifying the session number. For example, if there is a shared section called ShimSharedMemory that session 4 uses, you can access the session using Sessions4BaseNamedObjects ShimSharedMemory.

Sometimes, the idea of local EoP attacks is dismissed because people might believe such an attack requires an attacker to have physical access to the machine, the entire machine, or just part of the machine, such as a computer kiosk. The reasoning is if an attacker has physical access to the machine, there are worse problems to consider than a local EoP. Although this excuse might be valid in some cases, it is simply a lazy approach to application security. A user should not be able to take advantage of an application’s weak permissions to elevate permissions—period. Also, it is important to realize that a user doesn’t always have to have physical access to a machine to log on to it; the attacker could use a terminal server to access the machine, and this would still be considered a local attack because the attacker must be logged on to the machine to pull off the attack.

On February 8, 2005, Microsoft released Security Bulletin MS05-012 (http://www.microsoft.com/technet/security/Bulletin/MS05-012.mspx), which discussed an EoP attack enabled by a vulnerability that existed in the way the Windows operating system and programs accessed memory when processing Component Object Model (COM) structured storage files and objects. Using this attack, an attacker, who had to be logged on to the machine, could take complete control of the system.

Other Permissions Considerations

In addition to using access controls on securable objects, you can use other methods to protect resources. A few common methods include these:

  • Microsoft .NET permissions

  • SQL permissions

  • Role-based security

.NET Permissions

Using Microsoft .NET Framework permissions is a way to protect a resource when accessing it from managed code. Not only can you have role-based security that describes the privileges of a user, you can also use code access security to limit the access of the application’s resources. For example, .NET applications can deny permission to the registry, thus preventing the application from accessing the registry. Chapter 15 covers the complex .NET Framework security model.

SQL Permissions

Almost all objects in Microsoft SQL Server can be secured by using permissions, much like ACLs can be set on securable objects in the Windows operating system. When an action is performed in SQL, the permissions for the user account executing the query are determined. For instance, when a user runs a stored procedure, reads or writes data, or creates a database or database item, the user must have the appropriate level of permissions.

SQL Server can use either defined SQL accounts or Windows accounts to grant permissions to a database. A user can be granted server-wide roles, in which they have powerful permissions over the entire server. In addition to server-wide roles, a user can be granted permission to a specific database. A user can be granted or denied permissions for objects in a database. Figure 13-8 shows the permissions for a user called test. The user is granted Execute permission on a stored procedure called proc_Test and is denied access on calling SELECT on the testTable table.

Permissions on testdatabase for the user test

Figure 13-8. Permissions on testdatabase for the user test

What happens if the stored procedure called proc_Test does a “SELECT * FROM testTable” statement? Because the user is granted Execute permissions, the command succeeds even though the user is denied the SELECT command on testTable. Figure 13-9 shows the result in SQL Query Analyzer.

By restricting the permissions on the database for the user that connects to the SQL Server, you are effectively restricting what the user accounts can do on the database server. This method of defense is extremely useful if you have a Web application that uses a back-end SQL Server because a SQL injection bug will then be limited in how an attacker can use it. Refer to Chapter 16, for more information on SQL injection attacks.

Important

The Public role in SQL is equivalent to the Everyone group in the Windows operating system.

How the stored procedure still executes even though permissions are denied for the SELECT command

Figure 13-9. How the stored procedure still executes even though permissions are denied for the SELECT command

SQL Triggers

In addition to SQL permissions, an application might use SQL triggers to add business logic to restrict permissions when executing a query. The SQL engine automatically calls the triggers when data in a table is added, deleted, or modified; however, triggers cannot be used to determine whether a SELECT statement was executed.

SQL Security Functions

SQL Server provides security functions that can be used in a query to return information about users and roles. These include the following:

  • IS_MEMBER. Indicates whether the current user is a member of a specified Windows group or SQL Server role

  • IS_SRVROLEMEMBER. Indicates whether the current user is a member of a specified server role

Using such methods can provide a different type of access control policy.

Global Temporary Stored Procedures

SQL Server supports two types of temporary procedures: local and global. A local temporary procedure is available only to the user that created it, but a global procedure is available to everyone. The problem with using global temporary stored procedures is that all users can access them, and the permissions cannot be revoked. Also, a global temporary stored procedure can be altered by anyone. Think of all the problems such vulnerability can cause, especially if the user account that is calling the global temporary stored procedure is a high-privileged account.

Global temporary stored procedures are created like any other normal stored procedure, but the procedure name is preceded by two pound signs (such as ##procedure_name).

To find whether any global temporary stored procedures were created, you can search the SQL queries your application executes and look for something like this:

CREATE PROC ##<procedure_name>

Or you can run the following query to see whether any temporary procedures are currently created in the tempdb database:

SELECT name FROM tempdb..sysobjects WHERE name LIKE '##%'

Important

Your application should avoid using temporary global stored procedures because they can easily be modified by a malicious user.

SQL Server also allows the creation of global views and tables. These are created similarly to how global temporary stored procedures are, prefixing the name with ## when creating the object. The preceding SQL query can also help you find any global views and tables that are created.

Role-Based Security

Your application might implement its own permission model using a role-based technique. Somewhat like Windows groups, a role-based security model grants access to particular users. However, instead of using ACLs, the application can use its own mechanism to protect resources. Applications can still use the Windows operating system to handle user authentication, but can combine business logic and role-based authorization to determine the access level of a user for particular features. For example, a restaurant might use an application to manage such items as seat assignments, orders, and inventory. The application might define roles and their functions as shown in Table 13-3.

Table 13-3. Example Roles for Restaurant Application

Role

Description

Attendant

Assigns seats to guests; makes reservations

Server

Creates orders; handles bills

Cook

Reads orders; updates status of orders

Manager

Does all of the above; refunds customers; handles inventory

When users log on to the restaurant application, the functions they can complete are limited to the roles they are assigned. For example, a Server cannot refund a customer; only the Manager can do that.

Testing an application that uses role-based security is a lot like testing the permissions on a securable object in the Windows operating system. Make sure that the privileges granted to each role make sense, and ensure the application enforces the permissions.

Summary

Permissions are a fundamental method used to protect objects from being compromised by an attacker. Although applications can put up several layers of defense to protect themselves, assume an attacker can break through them all—after all, no software is perfect. However, if you can block attackers’ access to resources by using permissions correctly, you make it a lot harder for attackers to compromise the system.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset