Group Policy Preferences Passwords Continued

For the original post, see here.

So in yesterday's post, I mentioned that this guy wrote a neat tutorial and Powershell script called Get-GPPPasswords.ps1 that will decipher the passwords in a valid Groups.xml file.  You can find his scripts here. (The PowerSploit repository on Github.)  I wrote an additional function to go inside of Get-GPPPasswords this morning.  The purpose of the new function is to automatically search your own domain for Groups.xml files, and use Get-GPPPasswords on them.  This can be handy for finding all the Groups.xml files as quickly as possible, especially in a domain with lots of policies.  And especially if you're pressed for time.  It's very simple:

function Find-GPPPasswords 


Scan your own domain in search of valid Groups.xml files in SYSVOL. If found, use Get-GPPPassword on them.
Author: Ryan Ries (


PS C:\> . .\Get-GPPPassword.ps1
PS C:\> Find-GPPPasswords
	Write-Host "Now searching $Env:UserDNSDomain for Group Policy Preferences passwords..."
	$GroupsFiles = Get-ChildItem -Path "\\$Env:UserDNSDomain\SYSVOL" -Recurse -Include Groups.xml
	foreach($_ in $GroupsFiles)
		Get-GPPPassword -Path $_		

Group Policy Preferences Passwords

Hello again.  Today I want to talk about Group Policy Preferences Passwords.

So as most of you probably know, you can manage local accounts across many computers using Active Directory Group Policy Preferences. You can create them, delete them, and change their passwords.

GPP Adding a User*About to add a local user to all computers to whom this GPO is linked.*

This feature can also be really useful for adding already existing AD security groups to, say, the local administrators group on all the machines.  For instance, I could use this console to add the "DOMAIN\Accounting Managers" group to the local Administrators group on all the computers in the Accounting OU. That sort of thing.  However, one slightly more interesting piece of knowledge is that local users created here, will have their passwords stored in SYSVOL, in a format that is decipherable.

I've been seeing this "news" pop up around the internet here and there the past couple of weeks, so I wanted to speak to it.  Here is one such article on how to "crack" the passwords stored via GPP. Here's another one.  Now I want to make clear that these people are not "hacking" or "exploiting" Windows 2008 Group Policy Preferences by pointing this out.  This is well-known functionality that is fully documented by Microsoft.  Microsoft has warned about this, more than once, and even posts the AES decryption key themselves on MSDN!

I suppose that MS could pop up a warning dialog when editing that particular GPP item, plainly telling the administrator that this should not be used for sensitive, administrative accounts.  I have no doubt that there are companies of all sizes that are using this today without realizing that it's not secure.  Even though it is fully documented, you can't count on people reading the documentation.  RTFM is as true now as it was in 1979.

It's important to remember that this code was inherited by Microsoft when they acquired another software company, not originally written by Microsoft.  MS bought what used to be known as PolicyMaker, and integrated their stuff into what we know now as Group Policy Preferences.  Since there were already customers using PolicyMaker, that bit with the not-so-secure passwords needed to stay in so as to preserve compatibility with existing customers. So MS is aware, they made a deliberate choice to leave that functionality in knowing that it was insecure, and it will hopefully get improved down the road.

As they say, "compatibility is deliberately repeating someone else's mistakes."

Windows 2008 R2 + SQL 2008 R2 + Password Policy = Security Event Log Out of Control

I was asked to troubleshoot an interesting problem today where the Windows Security event log was being "flooded" by one particular sort of event.  By flooded, I mean about 20 duplicate entries logged per second.  The biggest problem with this is that it was making the Security log on that server useless, as the log would fill up within 45 minutes at that rate.  Click to enlarge the screenshot below:

event log ss*Names were changed to protect the guilty*

 The operating system is Windows 2008 R2. The server is a domain member. The server also runs SQL Server 2008 R2.  The server is a cluster node in a failover cluster.  I started Googling and Binging the event ID and description, and I didn't get much at first.

As an aside, I can't believe I just used the word "Binging," and I much prefer Google for almost everything, but if you want to search Technet, MSDN, and other Microsoft sites, the built-in Bing search on those sites actually does tend to produce better results on those sites than a general Google search. For me anyway. Maybe a " xyz" search on Google would do just as well. Anyway... onward:

So the only solid clue I found in my searching was this Technet article. The Windows Password Policy Checking API was being called at a staggering rate, but why?  By whom?  How do I make it stop?  The same SQL service account was being named in the events, so it obviously must have something to do with SQL.  Well, I could turn off the auditing of "Other Account Management Events," either by way of domain GPO or local security policy on that server... but that would only suppress the logging of that behavior.  It does nothing to stop the actual behavior.  Plus I would also lose any other events of that same category on the system.

I also knew that there was an "Enforce Password Policy" option that can be configured on each SQL account.  So I fired up SQL Management Studio on that server and did some testing, and as it turns out, that option was enabled on several accounts, including service accounts that are designed to hit the database rapidly.  It appears that every time an authentication attempt is made by one of those SQL accounts that has that Enforce Policy option checked, the SQL service makes a call to that Windows API to do some password policy checking, and that event is logged.

I tactically identified a few key service accounts that I knew to be very active on that database, and I disabled the "Enforce Password Policy" option on those accounts one by one.  I confirmed that with each account I changed, the rate at which those Security event 4793's were coming in decreased. Until finally, they stopped completely.


That's all for today.  On one final note, I wish we did cool things like this in the United States, especially as a resident of a state that cuts science funding.

From the "No Not All My Stuff Is Patched Yet" Dept.

This is a public service announcement.

If you have a Windows 2008 R2 server without SP1 or without this hotfix, and you also have a monitoring application or script that uses WMI to query for service information from the Win32_Service WMI class on a regular basis, you might just get burned really badly by a memory leak that will have the WMI service gobbling up >500MB of memory until it crashes.  You might then have to roll back hours of work and hope the Microsoft bug didn't cause any cluster failovers or dropped Exchange mailbox stores across a couple dozen servers.

Transitioning From VB Script to Powershell

VB Script is still around and will be for quite a while yet.  But current Windows technology is all about Powershell.  As well it should be, as PS is vastly superior in many, many ways.

However, a lot of us still have old VB scripts hanging around, probably doing production work... and what I’m about to show you may be the trickiest part of porting those old scripts over into Powershell. 

As you probably know, Powershell fully harnesses the power and flexibility of .NET, while VB Script was only capable of working with COM objects.  Almost everything that can be done with COM objects can be done faster and easier with .NET.  (For the foreseeable future at least - I hear COM is making a bit of a comeback in Windows 8...)  However, Powershell is still fully capable of working with COM objects too.  What that means is that those of you who are still more comfortable with VB script or have a lot of script to port over in a hurry, well, you don’t have to worry about finding .NET equivalents for those COM objects. (Even if there might be a better, more Powershell-native way of doing it.) 

Let’s take Microsoft Cluster Services for example.  Here’s what you would see in a VB script that deals with cluster resources: 


Set oCluster = CreateObject("MSCluster.Cluster")


 In Powershell it’d be something like this: 


$cluster = New-Object –COMObject MSCluster.Cluster


 Now  you have your cluster object.  Want to see what all members it has?  (The properties of it + its methods/what all it can do?) 


$cluster | Get-Member


 Alright well I see that $cluster is basically an object collection that has, among other things, a ResourceGroups object in it, so let’s open that up: 


$ResourceGroups = $cluster.ResourceGroups


 And then do a $ResourceGroups | Get-Member to see what we can do with that: 


PS C:\Users\ryan> $resourceGroups | Get-Member

   TypeName: System.__ComObject#{f2e60706-2631-11d1-89f1-00a0c90d061e}

Name                MemberType Definition
----                ---------- ----------
Delete              Method     void Delete ()
Move                Method     Variant Move (Variant, Variant)
Offline             Method     Variant Offline (Variant)
Online              Method     Variant Online (Variant, Variant)
Cluster             Property   ISCluster Cluster () {get}
CommonProperties    Property   ISClusProperties CommonProperties () {get}
CommonROProperties  Property   ISClusProperties CommonROProperties () {get}
Handle              Property   ULONG_PTR Handle () {get}
Name                Property   string Name () {get} {set}
OwnerNode           Property   ISClusNode OwnerNode () {get}
PreferredOwnerNodes Property   ISClusResGroupPreferredOwnerNodes PreferredOwnerNodes () {get}
PrivateProperties   Property   ISClusProperties PrivateProperties () {get}
PrivateROProperties Property   ISClusProperties PrivateROProperties () {get}
Resources           Property   ISClusResGroupResources Resources () {get}
State               Property   CLUSTER_GROUP_STATE State () {get}

So hopefully this is starting to pique your interest.  With this sort of information you could easily script out whether all the cluster resource groups were on the correct nodes, and even move them if need be.  Pretty neat stuff.

I leave you with this - don't you hate it when this happens?

F'ed up log