WMI Queries: ReturnValue vs uValue (and some Remote Registry)

This blog is updated at https://ciberesponce.com


Interestingly, when querying a registry setting in Windows via WMI, through PowerShell, it isn't as straight forward as previously thought. When developing the Audit Policy settings tool, which uses WMI through PowerShell explicitly to avoid dependencies on Windows Remote Management (WinRM) or even the Remote Registry service, we hit a bug in our logic.

What we discovered can be summarized here:

uValue will give you the raw value.  Does the key exist or not, and if it does, whats the value? Seems straight forward... but how do you know what the default OS setting is--which also could mean the key doesn't even exist!? That is where ReturnValue comes in--even if the key doesn't exist, it will return the default OS setting.  What makes this confusing is, if you explicitly create the key, even if it mimics the default OS value, ReturnValue changes!

So, what did we need to do to get around this?

Reproducing the bug

Default Behavior

By default, Server 2008+ has Advanced Audit Policies Enforced. That means, even without the "SYSTEM\CurrentControlSet\Control\Lsa\scenoapplylegacyauditpolicy" registry key, the system will give it a value of 1. When you query the DC without this value (meaning the default value of the OS), you get:

For the Property "ReturnValue", we get a 1. For the "uValue" we get a 0.

Explicitly setting value to 1 (Enforced is enabled, same effect as no value/OS-default)

However, when we explicitly set the policy with a 1 (meaning still enforced), we get these results:

ReturnValue now has a value of '0'. However, uValue has a value of '1'. Well this is interesting…

Explicitly setting value to 0 (disabling it; not a security best practice)

Now, for testing purposes, lets explicitly disable the Enforcement of Advanced Audit settings by setting the "scenoapplylegacyauditpolicy" value to '0'. Here is the result we get this time:

This time neither value is 1.

Our new logic…

Based on the above, we need to update our logic; "ReturnValue" property is no longer as useful to us anymore. I could have checked either property to see if they are "1" and if so, all is good. However, by just taking the uValue, I can:

  • If uValue == 0: Enforcement is disabled
  • Else (1 or nul): Enforcement is enabled

What about Remote Registry

Remote Registry, through testing, will always give you the "uValue".



Special thanks to Kurt Falde (whose GitHub Audit checker I used as a reference for part of this), Brian Mahaffey and Jon Stevenson.


Happy hunting (or in this case, measuring compliance so you can effectively hunt)!

Andrew (@ciberesponce)