Thursday, March 14, 2019

Windows Kernel Logic Bug Class: Access Mode Mismatch in IO Manager

Posted by James Forshaw, Project Zero

This blog post is an in-depth look at an interesting logic bug class in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Windows Kernel and what I did to try to get it fixed with our partners at Microsoft. The maximum impact of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug class is local privilege escalation if kernel and driver developers don’t take into account how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO manager operates when accessing device objects. This blog discusses how I discovered cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug class and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 technical background. For more information about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r investigation, fixing and avoiding writing new code with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug class refer to MSRC’s blog post.

Technical Background

I first stumbled upon cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug class while trying to exploit issue 779. This issue was a file TOCTOU which bypassed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 custom font loading mitigation policy. The mitigation policy was introduced in Windows 10 to limit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 impact of exploitable font memory corruption vulnerabilities. Normally it’d be trivial to exploit a file TOCTOU issue using a combination of file and Object Manager symbolic links. An exploit using symbolic links worked as a normal user, but not inside a sandbox. Racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than spend too much time on this low-impact issue, I exploited it without symbolic links using Shadow Object Directories and Microsoft fixed it as CVE-2016-3219. I added a note of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unexpected behavior to my list of topics to follow up on at a later date.

Fast forward over a year I decided to go back and take a look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unexpected behavior in more depth. The code which was failing was similar to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following:

HANDLE OpenFilePath(LPCWSTR pwzPath) {
 UNICODE_STRING Path;
 OBJECT_ATTRIBUTES ObjectAttributes;
 HANDLE FileHandle;
 NTSTATUS status;

 RtlInitUnicodeString(&Path, pwzPath);
 InitializeObjectAttributes(&ObjectAttributes,
                            &Path,
                            OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE);
 status = IoCreateFile(
              &FileHandle,
              GENERIC_READ,
              &ObjectAttributes,
              // ...
              FILE_OPEN,
              FILE_NON_DIRECTORY_FILE,
              // ...
              IO_NO_PARAMETER_CHECKING | IO_FORCE_ACCESS_CHECK);
 if (NT_ERROR(status))
   return NULL;
 return FileHandle;
}

When this code tries to open a file with an Object Manager symbolic link in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 path cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call to IoCreateFile failed with STATUS_OBJECT_NAME_NOT_FOUND. Furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r digging led me to discover cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 error in ObpParseSymbolicLink, which looks like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following:

NTSTATUS ObpParseSymbolicLink(POBJECT_SYMBOLIC_LINK Object,
                             PACCESS_STATE AccessState,
                             KPROCESSOR_MODE AccessMode) {
 if (Object->Flags & SANDBOX_FLAG
   && !RtlIsSandboxedToken(AccessState->SubjectSecurityContext, AccessMode))
   return STATUS_OBJECT_NAME_NOT_FOUND;

 // ...
}

The failing check is part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 symbolic link mitigations Microsoft introduced in Windows 10. I was creating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 symbolic link inside a sandbox which would set SANDBOX_FLAG in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 object’s structure. When parsing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 symbolic link while opening cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 font file this check is made. With cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sandbox flag set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel also calls RtlIsSandboxedToken to determine if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller is still inside a sandbox. As cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call to open cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 font file is in a sandboxed process thread RtlIsSandboxedToken should return TRUE, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function would continue. Instead it was returning FALSE, which made cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel think cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call was coming from a more privileged process and returns STATUS_OBJECT_NAME_NOT_FOUND to mitigate any exploitation.

At this point I understood how my exploit was failing, and yet I didn’t understand why. Specifically I didn’t understand why RtlIsSandboxToken was returning FALSE. Digging into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function gave me an important insight:

BOOLEAN RtlIsSandboxedToken(PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
                           KPROCESSOR_MODE AccessMode) {
 NTSTATUS AccessStatus;
 ACCESS_MASK GrantedAccess;

 if (AccessMode == KernelMode)
   return FALSE;
 
 if (SeAccessCheck(
        SeMediumDaclSd,
        SubjectSecurityContext,
        FALSE,
        READ_CONTROL,
        0,
        NULL,
        &RtlpRestrictedMapping,
        AccessMode,
        &GrantedAccess,
        &AccessStatus)) {
   return FALSE;
 }

 return TRUE;
}

The important parameter is AccessMode, which is of type KPROCESSOR_MODE and can be set to one of two values UserMode or KernelMode. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AccessMode parameter was set to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value KernelMode cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function would automatically return FALSE indicating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current caller is not in a sandbox. Breakpointing on this function in a kernel debugger confirmed that AccessMode was set to KernelMode when being called from my exploit. If this parameter was always set to KernelMode how would RtlIsSandboxToken ever return TRUE? To understand how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel is functioning let’s go into more depth on what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AccessMode parameter represents.

Previous Access Mode

Every thread in Windows has a previous access mode associated with it. This previous access mode is stored in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PreviousMode member of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 KTHREAD structure. The member is accessed by third-parties using ExGetPreviousMode and returns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 KPROCESSOR_MODE type. The previous access mode is set to UserMode if a user mode thread is running kernel code due to a system call transition. As an example cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following diagram shows a call from a user mode application to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system call NtOpenFile, via a system call dispatch stub function in NTDLL.
Note how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous mode is always set to UserMode even when code is executing inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NtOpenFile system call in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel memory space. In contrast, KernelMode is set if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread is a system thread (in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 System process) or due to a system call transition from kernel mode. The following diagram shows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 transition when a device driver (which is already running in kernel mode) calls cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ZwOpenFile system call, which results in executing NtOpenFile.

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 diagram a user mode application calls a function inside a device driver, for example using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NtFsControlFile system call. The previous mode equals UserMode during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device driver. However, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device driver calls ZwOpenFile cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel simulates a system call transition, this results in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous mode being changed to KernelMode and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NtOpenFile system call code being executed.

From a security perspective cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous access mode influences two important but fundamentally different security checks in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel, Security Access Checking (SecAC) and Memory Access Checking (MemAC). SecAC is used for calls to APIs exposed by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Security Reference Monitor such as SeAccessCheck or SePrivilegeCheck. These security APIs are used to determine if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller has rights to access a resource. Commonly cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 APIs take an AccessMode parameter, if this parameter is KernelMode cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 access checks pass automatically, which might be a security vulnerability if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resource couldn’t normally be accessed by a user. We already saw this use case in RtlIsSandboxToken, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 API checked explicitly for AccessMode being KernelMode and returned FALSE. Even without cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shortcut, by passing KernelMode to SeAccessCheck cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call will succeed regardless of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller’s access token and RtlIsSandboxToken would have returned FALSE.

MemAC is used to ensure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user application can’t pass pointers to a kernel address location. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AccessMode is UserMode cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n all memory addresses passed to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system call/operation should be verified that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y're less than MmUserProbeAddress or via functions such as ProbeForRead/ProbeForWrite. Again if this checking is incorrect privilege escalation could occur as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user could trick kernel code into reading or writing into privileged kernel memory locations. Note that not all kernel APIs perform MemAC, for example cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SeAccessCheck assumes that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller has already checked parameters, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AccessMode parameter is only used to determine whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r to bypass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 security check.

Storing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous access mode on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread creates a problem as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no way of differentiating between SecAC and MemAC for a kernel API. It’s possible for an API to disable SecAC intentionally and disable MemAC accidentally resulting in security issues and vice-versa. Let’s look in more detail how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO Manager tries to solve this mismatched access checking issue.

IO Manager Access Checking

The IO Manager exposes two main API groups for directly accessing files. The first APIs are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system calls NtCreateFile/ZwCreateFile or NtOpenFile/ZwOpenFile. The system calls are primarily for use by user-mode applications, but can be called from kernel mode if needed. The ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r APIs are exposed only for kernel mode callers, IoCreateFile and IoCreateFileEx.

If you compare cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementations of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 two main APIs you find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y’re simple forwarding wrappers around cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 internal function IopCreateFile. By default IopCreateFile uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current thread’s previous mode to determine whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r to perform MemAC and SecAC. For example when calling IopCreateFile via NtCreateFile from a user-mode process cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel performs MemAC and SecAC as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous mode will be UserMode. If kernel-mode calls ZwCreateFile cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous mode is set to KernelMode and both SecAC and MemAC are disabled.

IoCreateFile can only be called from kernel mode code and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no syscall transition involved, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore any calls will use whatever previous mode is set on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread. If IoCreateFile is called from a thread with previous mode set to UserMode this means that SecAC and MemAC will be performed. Enforcing MemAC is especially problematic as it means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel code can’t pass kernel mode pointers to IoCreateFile which would make cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 API very difficult to use. However cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller of IoCreateFile can’t just change cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread’s previous mode to KernelMode as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n SecAC would be disabled.

IoCreateFile solves this problem by specifying special flags that can be passed via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Options parameter. This parameter is forwarded to IopCreateFile but is not exposed through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NtCreateFile system call. Going back to our font issue, WIN32K is calling IoCreateFile and passing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 option flags IO_NO_PARAMETER_CHECKING (INPC) and IO_FORCE_ACCESS_CHECK (IFAC).

INPC is documented as:
“[If specified] cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parameters for this call should not be validated before attempting to issue cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 create request. Driver writers should use this flag with caution as certain invalid parameters can cause a system failure. For more information, see Remarks.”

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remarks section it expands furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r:
“The Options IO_NO_PARAMETER_CHECKING flag can be useful if a driver is issuing a kernel-mode create request on behalf of an operation initiated by a user-mode application. Because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 request occurs within a user-mode context, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 I/O manager, by default, probes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 supplied parameter values, which can cause an access violation if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parameters are kernel-mode addresses. This flag enables cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller to override this default behavior and avoid cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 access violation.”

This makes its purpose clear, it disables MemAC, allowing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel code to pass pointers into kernel memory as function parameters. As a byproduct it also disables most of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 validation of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parameters, such as incompatible flag combinations being checked. There is a separate, not properly documented, flag IO_CHECK_CREATE_PARAMETERS which turns back on just parameter flag checking, but not MemAC.

IFAC on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r hand is documented as:
“The I/O manager must check cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 create request against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file's security descriptor.”
This implies that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 flag reenables SecAC. It makes sense if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller was a system thread with previous mode set to KernelMode but why would we need to re-enable SecAC if we’re calling from UserMode? Here in lies cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 seeds to understanding cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original unexpected behavior, as we can see in some simplified code from IopCreateFile.

NTSTATUS IopCreateFile(PHANDLE FileHandle, ACCESS_MASK DesiredAccess,
                      POBJECT_ATTRIBUTES ObjectAttributes, ...,
                      ULONG Options) {
 KPROCESSOR_MODE AccessMode;

 if (Options & IO_NO_PARAMETER_CHECKING) {
   AccessMode = KernelMode;
 } else {
   AccessMode = KeGetCurrentThread()->PreviousMode;
 }

 FILE_PARSE_CONTEXT ParseContext = {};
 // Initialize ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r values
 ParseContext->Options = Options;

 return ObOpenObjectByName(
                 ObjectAttributes,
                 IoFileObjectType,
                 AccessMode,
                 NULL,
                 DesiredAccess,
                 &ParseContext,
                 &FileHandle);
}

This code shows that if INPC is specified cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AccessMode for all subsequent calls is set to KernelMode. Therefore specifying that option disables not just MemAC but also SecAC. It’s worth noting that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread’s previous mode is not changed, just cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AccessMode value which is passed forward to ObOpenObjectByName. IopCreateFile is delegating pointer checking to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Object Manager, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only way it can achieve this is to kill all checking. Crucially IFAC is not checked, it’s only passed forward inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parsing context structure, something for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO manager to deal with.

This isn’t cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 story just yet, it’s also possible to call ZwCreateFile and pass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 special flag OBJ_FORCE_ACCESS_CHECK (OFAC) inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OBJECT_ATTRIBUTES structure and ensure access checking is performed, even if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous access mode is set to KernelMode. As you can’t pass in IFAC via ZwCreateFile and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no checking for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OFAC flag in IopCreateFile, it must be in ObOpenObjectByName. Actually it’s slightly deeper, first all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parameters are processed based on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AccessMode passed to ObOpenObjectByName, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n ObpLookupObjectName is called which checks cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OFAC flag, if it’s set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n AccessMode is forced back to UserMode.

We can now finally understand why we got cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unexpected behavior with opening cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 font file. Parsing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 symbolic link happens inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Object Manager, not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO Manager, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore has no knowledge of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IFAC flag. IopCreateFile has told cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Object Manager to do all checks as if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous access mode is KernelMode, so that’s cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value passed to ObpParseSymbolicLink, which gets passed to RtlIsSandboxToken which rightly indicates it’s not running in a sandboxed process. However, once cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file is actually opened cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IFAC flag kicks in and ensures SecAC is still performed on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller had also specified OFAC cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 symbolic link would have worked, as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parsing would occur during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lookup operation which has been forced to UserMode.

This in itself is an interesting result, basically any operation which is called during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Object Manager parsing operation which trusts cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of AccessMode will disable security checks unless OFAC has been specified. However, that’s not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug that this blog is about, for that we need to go even deeper to work out how IFAC works inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO Manager.

IO Device Parsing

The Object Manager’s responsibility for opening a file ends once it finds a named device object in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Object Manager namespace. The Object Manager looks up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parse function for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Device type, which is IopParseDevice, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n passes all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 information it knows about. This includes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AccessMode value, which we know is set to KernelMode, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remaining path to parse and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parsing context buffer which includes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Options parameter. The IopParseDevice function does some security checks of its own, such as checking device traversal, allocates a new IO Request Packet (IRP) and calls cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver responsible for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device object.

The IRP structure contains a RequestorMode field which reflects cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AccessMode of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file operation. This reason to have a RequestorMode field is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IRP can be dispatched asynchronously. The thread which processes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO operation might not be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread which started cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO operation. You might guess now that this is where IFAC comes into play, perhaps cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO manager sets RequestorMode to UserMode? If you actually check this inside a kernel driver when accessed from IoCreateFile using INPC you’d find this field is still set to KernelMode, so it’s not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 answer.

The operation type being performed and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 operation’s specific parameters are passed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO Stack Location structure which is located immediately after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IRP structure. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case of opening a file cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 major operation type is IRP_MJ_CREATE and uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Create union field of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO_STACK_LOCATION structure. This is where IFAC comes in, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 flag is specified to IopCreateFile cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO Stack Location’s Flags parameter, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new flag SL_FORCE_ACCESS_CHECK (SFAC) will be set. It’s up to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file system driver to ensure it verifies this flag, and not rely on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 RequestorMode being set to UserMode. The NTFS driver knows this, and has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following code:

KPROCESSOR_MODE NtfsEffectiveMode(PIRP Irp) {
 PIO_STACK_LOCATION loc = IoGetCurrentIrpStackLocation(Irp);
 if (loc->MajorOperation == IRP_MJ_CREATE
     && loc->Flags & SL_FORCE_ACCESS_CHECK) {
   return UserMode;
 }
 else {
   return Irp->RequestorMode;
 }
}

The NtfsEffectiveMode is called by any operation which is going to perform a security related function. It ensures that SecAC is still performed even if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller was in kernel mode, as long as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IFAC flag was passed. The NTFS filesystem driver is a fundamental part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Windows OS, and has close interactions with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO manager, so it’s unsurprising it knows to do cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right thing. However, on Windows all drivers are filesystem drivers, even if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y don’t explicitly implement a filesystem.

I thought it’d be interesting to find out how many Microsoft and third-party drivers actually did cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right checks, or did cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y all just trust RequestorMode and base cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir security decisions on it?

Defining cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Bug Class

Finally we get to define cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug class. In order for a privilege escalation vulnerability to exist cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re needs to be two separate components.
  1. A kernel mode Initiator (code which calls IoCreateFile or IoCreateFileEx) which sets cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 INPC and IFAC flags but doesn’t set OFAC. This could be in a driver or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel itself.
  2. A vulnerable Receiver which uses RequestorMode during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 handling of IRP_MJ_CREATE for a security decision but doesn’t also check cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Flags for SFAC.


The following table summarises an API when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 calling thread’s previous mode is set to UserMode. The table includes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 state of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input options, INPC, IFAC and OFAC and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding IRP’s RequestorMode and SFAC flag. I’ve highlighted when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 calls are a useful initiator.

Method
INPC
IFAC
OFAC
RequestorMode
SFAC
Initiator?
IoCreateFile
NO
NO
NO
User
NO
NO
IoCreateFile
YES
NO
NO
Kernel
NO
YES
IoCreateFile
YES
YES
NO
Kernel
YES
YES
IoCreateFile
N/A
N/A
YES
User
N/A
NO
IoCreateFileEx
NO
NO
NO
Kernel
NO
YES
IoCreateFileEx
YES
NO
NO
Kernel
NO
YES
IoCreateFileEx
YES
YES
NO
Kernel
YES
YES
IoCreateFileEx
N/A
N/A
YES
User
N/A
NO

It’s worth noting that any calls where IFAC is not passed might be vulnerable to a privileged file access vulnerability as without cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 generated SFAC flag even NTFS would not perform security checks. There are ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r similar functions to IoCreateFile such as FltCreateFileEx which are used in special cases but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y all have similar properties. Also notice in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 table cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rules are slightly different for IoCreateFileEx. While it’s not documented, IoCreateFileEx always passes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 INPC option to IopCreateFile, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore unless cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 OFAC flag is specified it will always run its operations with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous access mode set to KernelMode.

The ideal Initiator is one which opens an arbitrary path from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user and gives full control over all parameters to IoCreateFile and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 opened file handle is returned back to user mode. However, depending on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Receiver full control might not be necessary.

A Receiver could perform a number of actions when receiving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IRP. Common for file system drivers is to parse cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remaining filename and perform some furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r action based on that such as opening a different file. Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r possibility is parsing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Extended Attributes (EA) block and performing some action based on that. It might just be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case that opening cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device object would normally require an access check which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 setting of RequestorMode to KernelMode bypasses.

Examples

Here’s some examples I’ve found of both Initiators and Receivers. This is based on code shipped with Windows 10 1709 which is two versions behind what’s available today (1809) but many of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se examples still exist in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 latest versions of Windows as well as Windows 7 and 8. All cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 examples are Microsoft code, so a third party developer probably has even less understanding of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 behavior.

To discover cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se examples I didn’t use any special static analysis tooling, instead I just searched for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m manually. I left deeper investigation to Microsoft.

Receivers

Finding Receivers can be much harder than Initiators as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no imported function to search for which gives a clear signal to look for. Instead I looked for drivers which imported IoCreateDevice to ensure cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver is exposing a device of some sort. I cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n filtered cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 drivers' imported APIs to ones which took an explicit AccessMode parameter, such as SeAccessCheck or ObReferenceObjectByHandle. Of course this didn’t really limit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of drivers very much so ultimately I had to manually analyze drivers which looked cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most interesting. In my analysis cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 “real” file system drivers such as NTFS and FAT always seem to do cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 right thing.

WS2IFSL

While not always enabled this driver is used to create a file object which passes read/write requests to a user mode application using APCs. When creating a new object you can specify an EA with information to create eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r a Socket or a Process file. The APC is set up in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CreateProcessFile function based on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 information in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 EA. The driver uses RequestorMode without any furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r checks, this would allow cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 callback APC to execute in kernel mode. When creating a process file you also pass a handle to a thread which is opened for THREAD_SET_CONTEXT access for use with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 APC. Setting KernelMode allows kernel handles to be used with call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call to ObReferenceObjectByHandle however as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread has to be in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 calling process this isn’t much of a benefit.

NTSTATUS DispatchCreate(DEVICE_OBJECT* DeviceObject, PIRP Irp) {
 PFILE_FULL_EA_INFORMATION ea = Irp->AssociatedIrp.SystemBuffer;
 PIO_STACK_LOCATION loc = IoGetCurrentIrpStackLocation(Irp);
 if (ea->EaNameLength != 7)
   return STATUS_INVALID_PARAMETER;
 if (!memcmp(ea->EaName, "NifsSct", 8))
   return CreateSocketFile(lock->FileObject, Irp->RequestorMode, ea);
 if (!memcmp(ea->EaName, "NifsPvd", 8))
   return CreateProcessFile(lock->FileObject, Irp->RequestorMode, ea);
 // ...
}

To be exploitable a compatible initiator must be able to provide an EA on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process file. Then a socket file would need to be created which referenced that process file, and a read/write operation must be performed to force cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 APC to execute. On modern versions of Windows 10, you’ll also have to worry about SMEP and Kernel CFG. If you point cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 APC routine at a user mode address cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel will bug check when it goes to execute cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 APC in KernelMode as shown in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following screenshot which I created by using a custom initiator to setup WS2IFSL.

NPFS

While NPFS correctly checks for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SFAC it does use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 RequestorMode to determine if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller is allowed to specify an arbitrary EA block. Normally when a named pipe is opened cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver records cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 calling PID and a session ID. This information can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n be exposed via APIs such as GetNamedPipeClientProcessId. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller is UserMode cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code isn’t allowed to set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 EA block, but if it's KernelMode an arbitrary EA block can be used which means cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PID and session ID fields can be spoofed.

NTSTATUS NpCreateClientEnd(PIRP Irp, ...) {
 // ...
 PFILE_FULL_EA_INFORMATION ea = Irp->AssociatedIrp.SystemBuffer;
 PVOID Data;
 SIZE_T Length;
 
 if (!NpLocateEa(ea, "ClientComputerName", &Data, Length))
   return STATUS_INVALID_PARAMETER;

 if (!IsValidEaString(Data, Length) || Irp->RequestorMode != KernelMode)
   return STATUS_INVALID_PARAMETER;

 NpSetAttributeInList(Irp, CLIENT_COMPUTER_NAME, Data, Length);
 NpLocateEa(ea, "ClientProcessId", Data, Length);
 NpSetAttributeInList(Irp, CLIENT_PROCESS_ID, Data, Length);
 NpLocateEa(ea, "ClientSessionId", Data, Length);
 NpSetAttributeInList(Irp, CLIENT_SESSION_ID, Data, Length);

 // ...
}

This behavior is used to allow cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SMB driver to set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 computer name field and session ID. If some service trusted this information it could be used to elevate privileges. To exploit this you’d need to be able to set an arbitrary EA as KernelMode, and to do something interesting you’d need to access cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 opened handle.

Initiators

To find Initiators I looked in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel and drivers for any functions which called IoCreateFile et al and did basic inspection of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call parameters for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Options and object attributes flags. With suitable candidates I was able to do a closer inspection to determine what parameters cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user could influence. Finding Initiators is relatively trivial once you understand cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug class as you can quickly narrow down targets just by looking for imported calls to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target methods.

NTOSKRNL NtSetInformationFile FileRenameInformation Class

When renaming a file you can specify an arbitrary path even though cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file can’t be on a different volume to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original. The function IopOpenLinkOrRenameTarget is called to open cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target path first using IoCreateFileEx passing INPC and normally IFAC (it also sets IO_OPEN_TARGET_DIRECTORY but that’s not important for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 operation). This initiator only allows you to specify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 full path.

SMBv2 Server Driver

The SMB servers will open files on a share using IoCreateFileEx, for example in Smb2CreateFile. It specifies IFAC but not INPC because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 call is made on a system thread so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous access mode is already KernelMode. Normally it’s not possible to redirect cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file creation to an arbitrary NT Object Manager path as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server passes a relative path to an open volume handle. While you could add a mount point to a directory on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file system and access cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server locally cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel intentionally limits cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target device to a limited set of types.

if (ParseContext->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT) {
 switch (ParseContext->TargetDevice){
   case FILE_DEVICE_DISK:
   case FILE_DEVICE_CD_ROM:
   case FILE_DEVICE_DISK:
   case FILE_DEVICE_TAPE:
     break;
   default:
     return STATUS_IO_REPARSE_DATA_INVALID;
 }
}

There was a bug in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation which allows you to circumvent cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 device check which allows a local mount point to be used to redirect cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SMB server to open any device file. The SMBv2 driver has special requirements when it comes to NTFS symbolic links, it must return cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 link information to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client for processing. To support cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 symbolic link feature cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server passes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Options flag IO_STOP_ON_SYMLINK as shown below.

NTSTATUS Smb2CreateFile(HANDLE VolumeHandle, PUNICODE_STRING Name, ...) {
 // ...
 int ReparseCount = 0;
 OBJECT_ATTRIBUTES ObjectAttributes;
 ObjectAttributes.RootDirectory = VolumeHandle;
 ObjectAttributes.ObjectName = Name;
 IO_STATUS_BLOCK IoStatus = {};
 do {
   status = IoCreateFileEx(
           &FileHandle,
           DesiredAccess,
           &ObjectAttributes,
           &IoStatus,
           ...
           IO_STOP_ON_SYMLINK | IO_FORCE_ACCESS_CHECK
         );
   if (status == STATUS_STOPPED_ON_SYMLINK) {
     UNICODE_STRING NewName;
     status = SrvGraftName(ObjectAttributes.ObjectName,
       (PREPARSE_DATA_BUFFER)IoStatus.Information, &NewName);
     if (status == STATUS_STOPPED_ON_SYMLINK)
       break;
     ObjectAttributes.RootDirectory = NULL;
     ObjectAttributes.ObjectName = NewName;
     continue;
   }
 } while(ReparseCount++ < MAXIMUM_REPARSE_COUNT);
 // ...
}

If IoCreateFileEx returns STATUS_STOPPED_ON_SYMLINK cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server extracts cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 returned REPARSE_DATA_BUFFER structure from IO_STATUS_BLOCK and passes it to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SrvGraftName utility function. The reparse buffer could be eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r a mount point or a NTFS symbolic link. If it’s a symbolic link cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n SrvGraftName returns STATUS_STOPPED_ON_SYMLINK again which allows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server to return cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 buffer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reparse buffer is a mount point cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n SrvGraftName builds a new absolute path based only on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 string found in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 REPARSE_DATA_BUFFER, it doesn’t check cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 destination device. The server reissues cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 open request with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new absolute path which can now point to any device on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system.

This initiator was cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 best I found during my analysis. You can specify almost all arguments to IoCreateFileEx including cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 EA buffer. This allows you to initialize a driver which requires an EA (such as WS2IFSL) which opens up a lot more attack surface. As it’s running on a system thread both cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 RequestorMode and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread’s previous mode are set to KernelMode which might introduce ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r interesting attack surface.

While impressive it’s not an ideal Initiator, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 opened device needs to support certain valid IRP’s such as IRP_MJ_GET_INFORMATION_FILE ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 server will not return a valid handle to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 caller. Without this check it would be trivial to exploit WS2IFSL as you’d be able to perform a Read/Write operation to get an APC to execute in kernel mode. Even if you can get a handle back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SMB Server intentionally limits cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IO control codes you can send which limits many of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 interesting things you could do with this vulnerability. Even so I considered this to be a serious issue, so I reported it directly to MSRC. It was fixed as CVE-2018-0749 by using a special Extra Creation Parameter which will filter out all ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r reparse points except for symbolic links.

Next Steps

While I believed this to be a serious bug class, it turned out that finding a matching Initiator/Receiver pair was very difficult. In my research I didn’t find any pair which would give direct privilege escalation. The best pair I identified was combining cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SMBv2 Initiator with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 NPFS process ID spoofing. While I couldn’t identity a service which would use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 client PID for any security related operation it’s possible that a third-party service exists.

I could have filed this issue back in my list of interesting or unexpected behaviors, to see if I could exploit it at a later date. But instead I decided to talk to my contacts at MSRC to see if we could collaborate instead. With MSRC on side I wrote up a document explaining cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug class and describing some of my findings. At cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same time I reported cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SMB server issue through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 normal channels at it was cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most serious issue I’d discovered. This led to meetings with various teams at Bluehat 2017 in Redmond where a plan was formed for Microsoft to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir source code access to discover cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 extent of this bug class in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Windows kernel and driver code base. Note, I did not have access to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source code, this part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 investigation was delegated to MSRC, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 results of which are in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir blog post.

It’s worth noting that while I applied cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 standard 90 day disclosure deadline to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SMB server report, I didn’t apply an explicit deadline to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug class report. With no single bug to point to it’d be difficult and likely counter productive to enforce such a deadline. However I did ensure that MSRC agreed to publish cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 technical details on this issue regardless of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 outcome. This is why we’re blogging about it now, 12 months after providing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 report.

Conclusions

It’s always interesting to find a new bug class in Windows to go hunting for. By luck this hasn’t turned out to be anywhere as serious as it could have been. While it would have made sense to specify two separate access modes, one for MemAC and one for SecAC, that’s not what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original NT design used. For backwards compatibility it seems unlikely that behavior will be changed. This bug class is as much about poor documentation as it is about technical issues, as while cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 behavior can’t be changed, it’s also poorly documented.

If you look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 documentation for IoCreateFile you’ll now find a new remark:
“For create requests originating in user mode, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 driver sets both IO_NO_PARAMETER_CHECKING and IO_FORCE_ACCESS_CHECK in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Options parameter of IoCreateFile cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n it should also set OBJ_FORCE_ACCESS_CHECK in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ObjectAttributes parameter. For info on this flag, see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Attributes member of OBJECT_ATTRIBUTES.”

This remark was added very recently. With cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 majority of Microsoft’s developer documentation on GitHub you can even find this commit which introduced it.

It’s likely that any bugs identified by MSRC will only be fixed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 latest versions of Windows 10, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore if you’re a developer you should read Microsoft’s blog post to understand how to avoid cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se issues in your drivers as well as techniques to find cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se sorts of issues in your code base. For security researchers it’s anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r thing to bear in mind when you’re reviewing a new Windows kernel driver.

I’d like to thank Steven Hunter and Gavin Thomas from MSRC who were my main points of contact for getting this bug class remediated.