Restricted Callers

Some kernel-mode functions that can be called from user mode are sensitive in that output they produce may contain kernel-mode addresses. Though this can happen accidentally, the typical case has a deliberateness to it in that the function exists to deliver information to user-mode processes that in turn exist for diagnostics and instrumentation. But relatively few user-mode processes have that purpose, none should be trusted more than can’t be avoided, and some are intentionally up to no good.

Revealing a kernel-mode address to a malicious user-mode caller is arguably not itself a security vulnerability, or not much of one, but it is useful armoury for an attacker because it may be the key to successfully exploiting some other vulnerability. To limit this, version 6.3 of the Windows kernel introduces the notion of a restricted caller that is not to be given kernel-mode addresses, nor even information from which to infer kernel-mode addresses.

Specially affected among native API functions is NtQuerySystemInformation. Some of its many information classes are made to fail for restricted callers. Others are permitted for restricted callers but with kernel-mode addresses omitted from whatever information is returned. A more subtle example is that NtCreateProfile and NtCreateProfileEx are failed for restricted callers who ask to profile kernel-mode execution.

What counts as a restricted caller depends on the Windows version. Originally, a restricted caller is specifically a low-integrity process. In detail, a process has low integrity if an integrity level cannot be obtained for the process’s primary token or if the integrity level is less than SECURITY_MANDATORY_MEDIUM_RID (0x2000). Windows 10 has a wider restriction that is essentially the same as implemented for the RtlIsSandboxedToken function: a user-mode caller is restricted unless it can pass an access check for READ_CONTROL rights to securable objects that have medium integrity.