To improve the security of our customers and the entire online community, the Microsoft Security Response Center ( MSRC ) looks into all reports of security flaws affecting Microsoft products and services. We value the excellent vulnerability research that the security community regularly reports to us and view working with these researchers as an honor.
James Forshaw of Google Project Zero is one researcher who consistently alerts us to high-quality, intriguing vulnerabilities. James ‘ work primarily focuses on intricate logic flaws in Windows internals, particularly those related to sandbox escapes and privilege escalation.
James and the MSRC team worked together on a novel bug class that he found in the Windows kernel and some of its drivers. Microsoft’s engineering teams fixed these bugs, and third-party driver developers can prevent introducing similar bugs.
Background
The system call handler in Windows records system calls made from user mode threads by setting the thread object’s PreviousMode field to UserModer. The PreviousMode of the thread will be set to KernelModE if the system call is made from a system thread or from kernel mode using zw-prefixed functions. To determine whether the caller’s arguments come from a trusted or untrusted source and, consequently, to what extent the kernel must validate them, this method of distinguishing between user mode and kernel mode callers is used.
A system call is made to NtCreateFile( ) or NtoOpenFiile whenever a user mode application creates or opens saas files. There are more API functions available in kernel mode code, including the IoCreateFile*, NtCreaateFile, and their Zw-prefixed equivalents. The I/O Manager can also perform these functions, as can the Filter Manager.
All of these lead to the internal I/O Manager function IopCreateFile, as shown in the diagram above. In IopCreateFile, the variable AccessMode is used to determine whether or not to check for valid parameters and buffers before passing it on to the Object Manager when calling ObOpenObjectByNameEx. Later, the AccessMode is used in IopParseDevice to check for access; if it’s user mode, a privilege check is carried out on the device object. IopParseDevice then creates an I/O Request Packet ( IRP ), assigns the AccessMode field to the RequestorMod, and uses IofCallDriver to give the device’s IRP_MJ_CREATE dispatch function control.
IopCreateFile has an Options parameter that can only be accessed by API functions in kernel mode and is not visible to callers of either NtCreaateFiile or NtoOpenFILE. Parameter validation is bypassed when the IO_NO_PARAMETER_CHECKING flag is set because it overrides the AccessMode and sets the thread’s previous mode to KernelMod. Additionally, this results in the later waiver of the privilege checks in IopParseDevice.
The IO_NO_PARAMETER_CHECKING flag is always set by IoCreateFileEx. This function is used to call into the I/O Manager for the FltCreateFile, FlcCREATEFILEEx, and FllccreateFiile2; these also always have IO_NO_PARAMETER_CHECKING set.
However, there are times when it is necessary to force access checks to take place in order to override this behavior. A kernel mode driver, for instance, may open an object name provided by a user mode application ( possibly via an IOCTL ).
The IO_FORCE_ACCESS_CHEC K flag setting on the Options parameter of IopCreateFile has two effects: first, it instructs the I/O Manager to run access checks in the manner of a UserMode without first setting it to one. Second, it causes the SL_FORCE_ACCESS_CHEC K to be set in the Flags field for the IRP_MJ_CREATE stack location. This flag is expected to be used by IRP_MJ_CREATE request handlers in their own access checks to bypass RequestorMode.
A new flag called OBJ_FORCE_ACCESS_CHEC K was introduced during the development of Windows XP because it became clear that other API functions operating in the object namespace ( such as ZwOpenKey for Registry ) needed a way to force an access check. This determines the requestor’s access mode to UserMode based on the attributes of the object being requested, rather than the I/O Manager. This override the effect of IO_NO_PARAMETER_CHECKING when setting KernelMode back in IopCreateFile and takes precedence over any access mode set that has already been set.
In a nutshell, the following:
-
An IRP_MJ_CREATE handler must determine whether to perform an access check by checking both the SL_FORCE_ACCESS_CHEC K flag and whether or not the RequestorMode of the IP is a user mode.
-
There are two ways for a kernel mode caller to specify that an access check should be carried out:
- By enabling the IO_FORCE_ACCESS_CHEC K Options flag in the I/O Manager, which in turn activates the Flags for IRP stack locations,
- By enabling the OBJ_FORCE_ACCESS_CHEC K OptionAttributes- >, flag, which changes the IRP’s RequestorMode to UserModer, you can do this from the object manager.
Vulnerability
James discovered through his research that different kernel mode drivers included with Windows that handled IRP_MJ_CREATE requests by checking the RequestorMode but notSL_FORCE_ACCESS_CHECK. Additionally, kernel mode code, which on the surface seems to be setting IO_FORCE_ACCESS_CHEC K when creating or opening a file, may be used to exploit these. A request from user mode could be used by an attacker to send an IRP_MJ_CREATE request with KernelMode as the Requestor Mode if they have enough control over the arguments of a file create/open call. A local privilege escalation vulnerability may result from using the RequestorMode check in a security decision.
James ‘ post on the Google Project Zero blog contains additional information, such as how he identified this vulnerability class and instances of such code appearing in the Windows kernel and drivers.
James identified two kernel mode code patterns: the “initiator,” which initiates a call and handles IRP_MJ_CREATE requests, and “receiver.” The following is a definition of these:
-
The following make up the “initiator”:
-
The following makes up the “receiver”:
- An IRP_MJ_CREATE request’s handler is:
-
- A security decision is made using the IRP’s RequestorMode.
- The Flags from the IRP’s stack are not subjected to this SL_FORCE_ACCESS_CHEC K testing.
To open a device object that the receiver manages, an attacker would need to be able to tell the initiator to do so. Because the Irp- >, RequestorMode, but not the SL_FORCE_ACCESS_CHEC K flag, the security check in the receiver is skipped over.
James had discovered instances of both initiators and receivers during his investigations, but none that would inevitably result in a rise in privileges. We decided to collaborate with him on additional research to see what we could discover.
Finding a variable
We used Semmle QL ( previously discussed on this blog here ) to search the source code for the aforementioned vulnerability code patterns for first-party drivers shipped with Windows ( drivers written by Microsoft ) and the Windows kernel itself.
We tracked the combinations of flags to Options and ObjectAttributes- >, attributes when passed to the internal function IopCreateFile, using a custom data flow analysis to identify initiator code patterns. The various file open API functions eventually arrive at this point, as was previously mentioned. Only the calls with the settings IO_FORCE_ACCESS_CHEC K and NO_PARAMETER-CHECKING were displayed in this result set due to filtering. We disregarded initiators that gave an attacker of the object name no control.
We looked at controlling expressions ( examinations used in control flow statements like if and switch ) that were reachable from either an IRP_MJ_CREATE dispatch or filter function and were influenced by the RequestorMode field. Expressions that required access to the Flags field of an IO_STACK_LOCATION object as well as the SL_FORCE_ACCESS_CHEC K macro were filtered out of these. In manual follow-up, a few RequestorMode checks were flagged as having no security implications ( for instance, when they were being used to forbid kernel mode callers rather than allow them ).
In the Windows source code, including those James had informed us of, this initial analysis discovered a total of 11 potential initiators and 16 potential receivers.
Additionally, Windows includes a large number of “inbox drivers “—third-party drivers that are essential for booting specific devices or enabling an unrestricted installation. To obtain a subset for further analysis, we filtered each driver binary’s import table. Since we were only interested in code that could be accessed via a device object or its filters, these were imports of IoCreateFile* or FltCREATEFILE* for the initiators and 0 and 1 for receivers, respectively. IDA Pro was used to analyze the remaining set of driver binaries. No additional initiators or receivers were discovered by this analysis.
Compatible initiators and receivers are needed to take advantage of these potential vulnerabilities. In order for an attacker to take advantage of the receiver, the initiator must specifically provide enough control over the eventual IopCreateFile call.
The receivers were divided into two groups, as we discovered:
-
requiring the provision of specific extended attributes in order to either complete a RequestorMode check or, after avoiding it in terms of exploitation, take action.
-
In order for the attacker to access code in its other potentially exploitable IRP dispatch functions, the file handle must be returned to them.
Fortunately, none of the initiators found during our analysis provided an attacker with the necessary skills.
In the following stage of our analysis, we conducted a more thorough search across all calls to the kernel mode file create/open APIs, including those to IO_NO_PARAMETER_CHECKING* and FltCreateFile*, which are all set to open. We narrowed down the hundreds of kernel and driver code results by concentrating on the two receiver categories after excluding all calls with OBJ_FORCE_ACCESS_CHEC K set.
To identify locations where extended attributes could be passed in, we first filter for calls where the EaBuffer parameter wasnon-NULL. In order to determine whether it might be possible to return a usable object handle to user mode, we also filtered calls where OBJ_KERNEL_HANDLE was not activated. As a result, the outcomes were manageable for manual analysis. However, within this result set, we were unable to locate any code that could be used as an initiator that was compatible.
comprehensive security measures for defense
In order to sum up James ‘ and MSRC’s combined investigations, it appeared that no Windows version currently supported contained an initiator and receiver combination that could be used for automatic local privilege escalation.
However, as a defense-in-depth measure, we decided to address these in upcoming Windows versions. The majority of these fixes are scheduled to be released in Windows 10 19H1, with a few being postponed for additional compatibility testing or because the component in which they are found is default deprecated and disabled.
We did think about making an API change so that, if IO_FORCE_ACCESS_CHEC K is set in Options, the IRP’s RequestorMode is automatically set to UserModer, just as it would have been with the OBJ_SOCIE_ATTACK attribute. This would be a broad fix to prevent instances of an initiator from happening. However, it was determined that the compatibility risk was too high for third-party drivers who might rely on the current behavior to break functionality.
information for developers of drivers
We advise all kernel driver developers to review their code to ensure proper IRP request processing and defensive use of the file open APIs because there is a chance that third-party drivers could fall victim to this vulnerability class.
The suggested adjustments ought to be fairly straightforward.
Do n’t rely solely on the RequestorMode value in IRP_MJ_CREATE dispatch handlers without first checking for the SL_FORCE_ACCESS_CHEC K flag. As an illustration, instead of:
if (Irp->RequestorMode != KernelMode) { // reject user mode requestors Status = STATUS_ACCESS_DENIED; }
Use the following example:
PIO_STACK_LOCATION ( IrpSp ) =IoGetCurrent
…
If user mode requestorsStatus = STATUS_ACCESS_DENIED and ( ( Irp- >, RequestorMode! = KernelModer ) | | ( InfSp, Flags, andamp )
Second, we strongly advise adding the OBJ_FORCE_ACCESS_CHEC K flag to ObjectAttributes in cases where it is already set in Options. As an illustration:
InitializeObjectAttributes ( &, FileName, ( OBJ_CASE_INSENSITIVE | OBJO_FORCE_ACCESS_CHECK) NULL, NUll )
Status = IoCreateFileEx ( andamp, ObjectHandle, GENERIC_READ | SyNCHRONIZE, amp, andamp; ioStatusBlock: NULL, 0, FILE_OPEN; 0, 0 Null; 1 / 2- 1; 2; 3 &: IO_FORCE_ACCESS_CHECK;
Set the OBJ_FORCE_ACCESS_CHEC K flag in ObjectAttributes to indicate that the thread’s previous mode is UserMode when a file create/open call is made on behalf of the user-mode request.
Acknowledgements
James Forshaw is to be commended for working with us on this vulnerability investigation as well as for providing the MSRC with numerous other excellent vulnerability reports.
The static analysis was scaled to include the entire Windows codebase with the help of Paul Brookes, Dileepa Kidambi Sudarsana, and Michelle Chen.
Steven Hunter, MSRC Vulnerabilities &, mitigation team