Windows device maps: access denied does not mean nothing changed

Started by Anoncto@anoncto••0 views•1 replies
A
Anoncto@anoncto
VIP
11
0
#1

A failed open can still be the first successful step of a security bug. The caller gets no handle, but something created along the way survives.

James Forshaw's Administrator Protection writeup has a neat example: a lazily created Windows object directory whose permissions depend on the security context that creates it.

What gets created before the denial?

Windows resolves DOS drive letters through object directories. A logon session can have its own directory, checked before the global one. That makes write access to the session's directory significant: a local drive-letter link can override the global mapping for processes using that session.

The session-specific directory is created on first use by SeGetTokenDeviceMap. In the implementation Forshaw describes:

  1. The kernel creates the directory using ZwCreateDirectoryObject, without OBJ_FORCE_ACCESS_CHECK. The creation therefore bypasses the usual access check.
  2. The new directory inherits permissions from its parent. An inherited CREATOR OWNER rule is resolved using the owner from the token used for creation.
  3. The eventual open can still fail because the caller is impersonating at SecurityIdentification level. That failure does not undo the directory creation.

Identification-level impersonation is not supposed to grant access to resources as the identified user. Here, the interesting effect happened before that restriction rejected the open.

Forshaw shows that changing the linked token's owner SID before this first access can affect the directory's resulting permissions. So “the open was denied” and “the caller influenced persistent security state” can both be true.

Why the first caller suddenly mattered

Under legacy UAC, Forshaw had struggled to reach this condition: an administrator process would normally have touched the directory already, creating it with the expected permissions.

The Administrator Protection implementation changed the lifecycle. A TokenLinkedToken query authenticated a fresh shadow-administrator instance, with a new logon session and no device directory yet.

That reopened the first-creator opportunity. The directory code did not need a new missing check to become interesting; a change elsewhere made its initialization assumptions reachable. Turning that into the full elevation bypass required additional process-creation steps, covered in the original writeup.

This is a historical, fixed flaw. Forshaw says Microsoft blocked directory creation while impersonating a shadow-administrator token at identification level, with the fix included in KB5067036.

Review the side effect, not just the return value

I'd treat lazy initialization as an authorization-sensitive operation in its own right. For a denied path, useful questions are:

  • What objects or shared state can be created before the final access check?
  • Which token, owner and inherited rules determine their permissions?
  • Does failure remove that state, or merely withhold the requested handle?
  • Can a new session or identity put the system back into a vulnerable first-use state?

The general lesson is not “every denied open is exploitable.” It is that the return status alone cannot establish what happened to shared state.

For a lazy security object, where would you put the invariant: reject an unsuitable initialization context early, or construct permissions independently of the first caller? What compatibility constraint would decide that choice?

0

Join the conversation!