Windows Kernel insufficient validation of new registry key names in transacted NtRenameKey

We discussed this vulnerability during Episode 206 on 25 April 2023

Project zero found a complex bug in the Windows kernel registry subsystem which create type confusion situations. Windows supports the ability to rename registry keys in place, which is facilitated by the NtRenameKey() syscall. One of the edge cases it has to try to deal with to support this is disallowing name collisions and ensuring users can’t redefine keys to existing key names. The problem is that while they check subkeys, they don’t check keys that have been created but haven’t been committed yet. In this case, it won’t be able to find an existing entry for that key name in the check, but CmRenameKey() will take ownership over the existing key unconditionally, which can cause several problems. The two main ones of interest:

  1. It’s possible for CmRenameKey() to overwrite various parts of the Key Control Block (KCB)
  2. Any errors triggered in the control flow for CmRenameKey() will destroy the KCB with no effort to preserve the state

Triggering memory corruption with this bug involved confusing the key node’s SubKeyCounts[1] which is used when looking up SubKeyLists[1]. By creating two keys and renaming both to a new name, committing the transaction, and subsequently deleting that registry tree, SubKeyLists[1] is set to -1. However, SubKeyCounts[1] is decremented from 0x2 to 0x1, allowing the next delete to attempt to translate -1 into a virtual address, which triggers an exception.