Select Page

If you haven’t already seen it, it’s now been widely documented that the feature within GP Preferences Group Policy that allows you to store passwords within a GPO for a variety of uses, is essentially not secure. If you haven’t read one of these posts, please do and familiarize yourself with the issue:

http://blogs.technet.com/b/grouppolicy/archive/2008/08/04/passwords-in-group-policy-preferences.aspx

http://www.grouppolicy.biz/2013/11/why-passwords-in-group-policy-preference-are-very-bad/

and most importantly, this document:

http://msdn.microsoft.com/en-us/library/cc232587.aspx

Which contains a PDF that actually documents the key (32-byte AES Encryption Key) used to encrypt the password within the SYSVOL portion of a GPO that specifies the password.  Specifically, the following areas within GP Preferences allow you to store passwords for a variety of tasks:

  • Data Sources
  • Local Users
  • Scheduled Tasks (both XP and New Style)
  • Services
  • Drive Maps

If you use any of these extensions, you might be exposing yourself to issues by allowing users with access to the GPOs that implement these settings, a possible avenue to decode passwords for what could be privileged accounts. If you do a web search on the phrase “Group Policy Preferences password exploit”, you will find all sorts of utilities that take advantage of the documented, static encryption key to decode password stored within GP Preferences settings. Great! Thanks! 🙂

So, now that we know that this previously awesome capability for managing passwords across your domain is now known to be pretty insecure, what do we do about it? Well, the first challenge is knowing how big the problem is. Essentially any GP Preferences settings in the 5 areas above could be subject to exploit if they are storing passwords using this weak encryption scheme.

So, what I’ve done is create a PowerShell function called Get-GPPCPassword, that will run through your entire domain’s GPOs, and locate where this password might be found across the 5 policy areas above. The output is a collection of PSObjects with 3 properties- the GPO Name, GPO Side (i.e. computer or user) and the policy area (of the 5 above) that has implemented a password. You’ll notice in the script below, that I’m leveraging the Microsoft Group Policy PowerShell module, and specifically the Get-GPOReport cmdlet, which we use with the -All parameter to get a collection of XML Settings reports for all GPOs in a given domain. Once we have the XML reports, we iterate through each report, using the PowerShell XML type accelerator, to find out whether the CPassword attribute exists within any of the 5 Policy Areas above. The script took about a minute and a half to run through about 300 GPOs in our test environment, but your mileage may vary since many of our GPOs are empty. The script is not super-optimized so you might find some logic improvements you can make in it when you play with it. The goal here is to provide a quick and dirty of finding out how bad things are, when it comes to using this particular feature within GP Preferences. Remediation of the problem is actually a pretty easy extension to this script, once you find the instances of it. I will leave that for another day!

Below you’ll find the function listing, as well as a link to the .ps1 script file itself, that you can download. The function takes a single optional parameter -DomainName, which can include a DNS Domain name of domain other than the default one you are running the script from. The best way to run this is to “dot-source” the script file by typing

. .\Get-GPPCPassword.ps1

From your PowerShell Console. Then you can simply call Get-GPPCPasssword or Get-GPPCPassword -DomainName myotherdomain.com from the prompt.

The output of the script, as I mentioned, is a collection of objects where the password preferences exist, as shown in this screenshot:

Retrieving GPOs that implement GP Preferences CPassword

Output of Get-GPPCPassowrd Script

So, here’s the download link for the script file: Get-GPPCPassword.PS1

And below is the script. Enjoy!

Darren

[codebox lang=”ps”]
#This script provided as-is with no warranty, by SDM Software, Inc. Copyright 2014. www.sdmsoftware.com
#for questions, contact info@sdmsoftware.com
#January, 2014

function Get-GPPCPassword {
param(
$DomainName #provide optional DNS domain name where you want to run report
)
Import-Module “GroupPolicy”
#create hashtables of extension names that contain CPassword extension property names and friendly Name
$computerExtensions = @{“LocalUsersAndGroups.User” = “Local Users and Groups”;
“DataSourcesSettings.Datasource” = “Data Sources”;
“NTServices.NTService” = “Services”;
“ScheduledTasks.Task” = “Scheduled Tasks”;
}
$userExtensions = @{“LocalUsersAndGroups.User” = “Local Users and Groups”;
“DataSourcesSettings.Datasource” = “Data Sources”;
“NTServices.NTService” = “Services”;
“ScheduledTasks.Task” = “Scheduled Tasks”;
“DriveMapSettings.Drive” = “Drive Maps”
}
$scheduledTaskTypes = @{“ScheduledTasks.Task” = “Scheduled Tasks”;
“ScheduledTasks.TaskV2” = “Scheduled Tasks (Vista and above)”;
“ScheduledTasks.ImmediateTask” = “Immediate Task (Windows XP)”;
“ScheduledTasks.ImmediateTaskV2” = “Immediate Task (Vista and above)”
}
# first, get GPO settings reports of all the GPOs in the selected domain
if ($DomainName -ne $null)
{
$GPOReports = Get-GPOReport -All -ReportType Xml -Domain $DomainName
}
else # run against current domain
{
$GPOReports = Get-GPOReport -All -ReportType Xml
}
# now iterate through all reports (i.e. GPOs) to find CPassword instances
for ($index = 0; $index -lt $GPOReports.Count; $index++)
{
[xml]$report = $GPOReports[$index]
#check computer extensions first
foreach ($extension in $report.GPO.Computer.ExtensionData)
{
foreach ($cExt in $computerExtensions.Keys)
{
if ($extension.Name -eq $computerExtensions[$cExt])
{
#create the standard command we’ll invoke for all extensions
$command = “`$report.GPO.Computer.ExtensionData.Extension.$cExt.Properties.cPassword”
#need to handle the special case for Scheduled where there could be multiple types
if ($extension.Name -eq “Scheduled Tasks”)
{
foreach ($schedTaskItem in $scheduledTaskTypes.Keys)
{
$command = “`$report.GPO.Computer.ExtensionData.Extension.$schedTaskItem.Properties.cPassword”
if ((Invoke-Expression -Command $command) -ne $null)
{
$obj = New-Object –typename PSObject
$obj | Add-Member –membertype NoteProperty –name GPOName –value ($report.GPO.Name) –passthru |
Add-Member –membertype NoteProperty –name Side –value (“Computer”) –passthru |
Add-Member –membertype NoteProperty –name Extension –value ($scheduledTaskTypes[$schedTaskItem])
Write-Output $obj
}
}
}
else
{
if ((Invoke-Expression -Command $command) -ne $null)
{
#Now create a new custom object containing the GPO Name, GPO side (computer or user) and extension where we found the password
$obj = New-Object –typename PSObject
$obj | Add-Member –membertype NoteProperty –name GPOName –value ($report.GPO.Name) –passthru |
Add-Member –membertype NoteProperty –name Side –value (“Computer”) –passthru |
Add-Member –membertype NoteProperty –name Extension –value ($extension.Name)
Write-Output $obj
}
}
}
}
}
#now check user extensions
foreach ($extension in $report.GPO.User.ExtensionData)
{
foreach ($cExt in $userExtensions.Keys)
{
if ($extension.Name -eq $userExtensions[$cExt])
{
#create the standard command we’ll invoke for all extensions
$command = “`$report.GPO.User.ExtensionData.Extension.$cExt.Properties.cPassword”
#need to handle the special case for Scheduled where there could be multiple types
if ($extension.Name -eq “Scheduled Tasks”)
{
foreach ($schedTaskItem in $scheduledTaskTypes.Keys)
{
$command = “`$report.GPO.User.ExtensionData.Extension.$schedTaskItem.Properties.cPassword”
if ((Invoke-Expression -Command $command) -ne $null)
{
$obj = New-Object –typename PSObject
$obj | Add-Member –membertype NoteProperty –name GPOName –value ($report.GPO.Name) –passthru |
Add-Member –membertype NoteProperty –name Side –value (“User”) –passthru |
Add-Member –membertype NoteProperty –name Extension –value ($scheduledTaskTypes[$schedTaskItem])
Write-Output $obj
}
}
}
else
{
if ((Invoke-Expression -Command $command) -ne $null)
{
#Now create a new custom object containing the GPO Name, GPO side (computer or user) and extension where we found the password
$obj = New-Object –typename PSObject
$obj | Add-Member –membertype NoteProperty –name GPOName –value ($report.GPO.Name) –passthru |
Add-Member –membertype NoteProperty –name Side –value (“User”) –passthru |
Add-Member –membertype NoteProperty –name Extension –value ($extension.Name)
Write-Output $obj
}
}
}
}
}
}

}
[/codebox]