Vista Improves Security Through Private Object Namespaces

You can protect Windows kernel objects such as events and mutexes by using security descriptors, but prior to Windows Vista you couldn't secure the actual names of the kernel objects. Building on the concept of namespaces that are used to separate Terminal Service sessions, Windows Vista allows you to define, secure, and use custom namespaces to prevent malicious applications from denying access to kernel object functionality.

To appreciate the security threats that private object namespaces address, it is worth first stepping back a bit and looking at how Windows names and uses kernel-level objects. If two processes need to have a high-performance data exchange, one option is to create and use a piece of global memory to share data. To prevent the multiple processes from inadvertently writing data at the same time, you can use a named mutex object for locking. You give the mutex a unique name that is known by both processes, and you can secure it with a security descriptor to prevent unauthorized access from other processes executing on the same machine. Kernel objects are identified only by name, so a process running under a very low privilege account can create a kernel object with the same name that a system process wants to use, effectively launching a denial-of-service (DOS) attack. The low-privilege process can create a mutex with the same name and acquire a lock that it never releases, preventing system processes that rely on a mutex of the same name from working properly.

The security weakness with kernel objects is that you cannot secure a name for a kernel object before creating the object. If the highly privileged process manages to create the kernel object first, you avoid a security problem because you can secure the object with a DACL. However, if the lower-privileged application creates the kernel object first, you have no way to close the object without the co-operation of all processes that have handles to the kernel object open, which will obviously be problematic when a malicious application is launching a DOS attack.

The solution is to create better security around the actual name of the kernel objects. Windows Vista achieves this by allowing you to create securable private namespaces. Private namespaces work the same way as the system-provided namespaces introduced in Windows 2000 to differentiate between kernel objects in various Terminal Server sessions. A namespace is identified by both a prefix (which has the same syntax as the system Global\ prefix) AND a security boundary descriptor. Because of this, a malicious client cannot simply modify its attack vector to now include namespace and kernel object name squatting, as the kernel treats namespaces of the same name but different security boundaries as different namespaces. Figure 1 illustrates this point.

Figure 1. Private Object Namespace

In Figure 1, the malicious application can create a namespace and kernel object of the same name, but it will not affect the use of kernel objects to synchronize data exchanges between the two server processes, which have different SIDs.

Using Private Namespaces

The syntax for using private namespaces is fairly simple. To create a server process (which would typically be in charge of creating the kernel objects and now has the added responsibility of securing them with a namespace), you can use the following code:

//create SECURITY_ATTRIBUTES for namespace boundary
SECURITY_ATTRIBUTES saKernelObjectSecurity;
saKernelObjectSecurity.nLength = sizeof(SECURITY_ATTRIBUTES);
//use the default security descriptor of the calling process
saKernelObjectSecurity.lpSecurityDescriptor = NULL;
saKernelObjectSecurity.bInheritHandle       = TRUE;

//create boundary
HANDLE hBoundaryDescriptor =
   CreateBoundaryDescriptor(_T("MC_BoundaryDescriptor"), 0);
if (NULL == hBoundaryDescriptor){
   printf("CreateBoundaryDescriptor failed. %d", ::GetLastError());
   return 1;
}

//get the local admin SID and add this to the boundary descriptor
PSID pLocalAdmin;
if (FALSE == ConvertStringSidToSid(_T("LA"), &pLocalAdmin)){
   printf("ConvertStringSidToSid failed. %d", ::GetLastError());
   return 1;
}

//add SID to boundary
BOOL res = AddSIDToBoundaryDescriptor(&hBoundaryDescriptor,
                                      pLocalAdmin);
LocalFree(pLocalAdmin);    //done with SID - free memory
if (FALSE == res){
   printf("AddSIDToBoundaryDescriptor failed. %d", ::GetLastError());
   return 1;
}

//create the namespace
HANDLE hNamespace = CreatePrivateNamespace(NULL, hBoundaryDescriptor,
                                           _T("MC"));
if (NULL == hNamespace){
   printf("CreatePrivateNamespace failed. %d", ::GetLastError());
   return 1;
}

//create mutex inside the namespace
HANDLE hMutex = CreateMutex(&saKernelObjectSecurity, FALSE,
                            _T("MC\\MyMutex"));
if (NULL == hMutex){
   printf("CreateMutex failed. %d", ::GetLastError());
   return 1;
}
else{
   printf("Successfully created mutex in private namespace");
}

printf("Press any key to quit");
char c;
gets_s(&c, 1);

CloseHandle(hMutex);
ClosePrivateNamespace(hNamespace, PRIVATE_NAMESPACE_FLAG_DESTROY);

return 0;

The code is relatively simple. It creates a namespace boundary with the default security attributes of the process, adds the SID of the local admin group to the boundary, and creates a namespace using the boundary. Once the boundary has been created, a kernel object can be created with the standard kernel functions—the only modification is the addition of the namespace prefix in the object's name.

The code required to open kernel objects in the namespace is almost identical, with the only differences being the use of the OpenNamespace function instead of the CreatePrivateNamespace. Once the private namespace has been opened, new kernel objects can be created with the prefix and existing objects can be opened using the prefix.

The key security feature is the namespace boundary and the associated SID—if the calling process is not within the group defined by the SID, a namespace cannot be created using the boundary. As the identity of a namespace is defined by both its boundary and string name, a process running at a lower privilege cannot conduct a kernel object name DOS attack.

Private Object Namespaces: Small But Crucial

While the average application developer isn't consumed by worry and stress about kernel object name squatting ruining his or her code-cutting career, the need for applications to achieve high-performance synchronization of the data they exchange is a real issue. Prior to private object namespaces, the possibility of malicious applications interfering with software systems such as virus scanners and DRM solutions was very real. As the fruits of Microsoft's big security push in the early part of the decade come to fruition in major product releases, the range of security-related functionality being released is certainly increasing, and private object namespaces are a small but crucial part of this push.

About the Author

Nick Wienholt is an independent Windows and .NET consultant based in Sydney, Australia. He is the author of Maximizing .NET Performance from Apress, and specializes in system-level software architecture and development with a particular focus on performance, security, interoperability, and debugging. Nick can be reached at NickW@dotnetperformance.com.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • Relying on outside companies to manage your network and server environments for your business and applications to meet the needs and demands of your users can be stressful. This is especially true as many Managed Hosting organizations fail to meet their service level agreements. Read this Forrester total economic impact report and learn what makes INetU different and how they exceed their customers' managed hosting expectations.

Most Popular Programming Stories

More for Developers

RSS Feeds