Of course it's a valid handle! (Or is it?)

How to use user-mode handle safely and protect kernel handles from misuse by user-mode applications.

Handles that are passed to a driver by a caller do not pass through the I/O Manager, so the I/O Manager cannot perform any validation checks on the handles. Never assume a handle is valid; always make sure the handle has the correct object type, appropriate access for the required tasks, the correct access mode, and that the access mode is compatible with the access requested.

Drivers should be careful when using handles, especially those received from user-mode applications. First, such handles are specific to a process context, so they're only valid in the process that opened the handle—when used from a different process context or from a worker thread, the handle could reference a different object or it could simply be invalid. Second, an attacker might close and reopen the handle to change what it refers to while the driver is using it. Third, an attacker might pass in such a handle to trick a driver into performing operations that are illegal for the application, such as calling ZwXxx functions. Access checks are skipped for kernel-mode callers of these functions, so an attacker can use this mechanism to bypass validation.

Drivers should also make certain that user-mode applications cannot misuse handles created by the driver. Setting the OBJ_KERNEL_HANDLE attribute for a handle makes it a kernel handle, which can be used in any process context but is only accessible from kernel mode (which is especially important for handles that are passed to the ZwXxx routines). A user-mode process cannot access, close, or replace a kernel handle.

What should you do?

  • After receiving any handle, call ObReferenceObjectByHandle immediately to swap the user-mode handle for an object pointer:
    • Always specify the object type you expect so you can take advantage of the type-checking provided by ObReferenceObjectByHandle.
    • For a user-mode handle, specify UserMode for AccessMode (assuming the user is expected to have the same access to the file object as your driver).
    • Always check the status code returned by ObReferenceObjectByHandle, and only proceed if it is STATUS_SUCCESS.
    • When you finish using the object pointer provided by ObReferenceObjectByHandle, call ObDereferenceObject to release the pointer and avoid a resource leak.
  • Before creating a handle for use in kernel mode, call InitializeObjectAttributes to initialize an OBJECT_ATTRIBUTES structure where Attributes has the value OBJ_KERNEL_HANDLE set.

The following code fragment shows proper usage of ObReferenceObjectByHandle, in this case for a handle to an event.

NTSTATUS status;
PKEVENT userEvent;
HANDLE handle;
handle = RetrieveHandleFromIrpBuffer(…);
status = ObReferenceObjectByHandle(handle,
    EVENT_MODIFY_STATE,
    *ExEventObjectType,
    UserMode,
    (PVOID*) &userEvent,
    NULL);
if (NT_SUCCESS(status)) {
    // do something interesting here
    KeSetEvent(userEvent, IO_NO_INCREMENT, FALSE);
    ObDereferenceObject(userEvent);
}

Kernel-Mode Drivers: Fixing Common Driver Reliability Issues

User-Mode Interactions: Guidelines for Kernel-Mode Drivers

InitializeObjectAttributes

ObReferenceObjectByHandle

ObDereferenceObject

 

 

Send comments about this topic to Microsoft