4104132150x0131358Microsoft-Windows-PowerShell/Operationalwin-dc-tcontreras-attack-range-677.attackrange.local7676.MakeByRefType())), (func netapi32 NetSessionEnum ([Int]) @([String], [String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())), (func netapi32 NetLocalGroupEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())), (func netapi32 NetLocalGroupGetMembers ([Int]) @([String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())), (func netapi32 DsGetSiteName ([Int]) @([String], [IntPtr].MakeByRefType())), (func netapi32 DsEnumerateDomainTrusts ([Int]) @([String], [UInt32], [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType())), (func netapi32 NetApiBufferFree ([Int]) @([IntPtr])), (func advapi32 ConvertSidToStringSid ([Int]) @([IntPtr], [String].MakeByRefType()) -SetLastError), (func advapi32 OpenSCManagerW ([IntPtr]) @([String], [String], [Int]) -SetLastError), (func advapi32 CloseServiceHandle ([Int]) @([IntPtr])), (func advapi32 LogonUser ([Bool]) @([String], [String], [String], [UInt32], [UInt32], [IntPtr].MakeByRefType()) -SetLastError), (func advapi32 ImpersonateLoggedOnUser ([Bool]) @([IntPtr]) -SetLastError), (func advapi32 RevertToSelf ([Bool]) @() -SetLastError), (func wtsapi32 WTSOpenServerEx ([IntPtr]) @([String])), (func wtsapi32 WTSEnumerateSessionsEx ([Int]) @([IntPtr], [Int32].MakeByRefType(), [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType()) -SetLastError), (func wtsapi32 WTSQuerySessionInformation ([Int]) @([IntPtr], [Int], [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType()) -SetLastError), (func wtsapi32 WTSFreeMemoryEx ([Int]) @([Int32], [IntPtr], [Int32])), (func wtsapi32 WTSFreeMemory ([Int]) @([IntPtr])), (func wtsapi32 WTSCloseServer ([Int]) @([IntPtr])), (func Mpr WNetAddConnection2W ([Int]) @($NETRESOURCEW, [String], [String], [UInt32])), (func Mpr WNetCancelConnection2 ([Int]) @([String], [Int], [Bool])), (func kernel32 CloseHandle ([Bool]) @([IntPtr]) -SetLastError) ) $Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Win32' $Netapi32 = $Types['netapi32'] $Advapi32 = $Types['advapi32'] $Wtsapi32 = $Types['wtsapi32'] $Mpr = $Types['Mpr'] $Kernel32 = $Types['kernel32'] Set-Alias Get-IPAddress Resolve-IPAddress Set-Alias Convert-NameToSid ConvertTo-SID Set-Alias Convert-SidToName ConvertFrom-SID Set-Alias Request-SPNTicket Get-DomainSPNTicket Set-Alias Get-DNSZone Get-DomainDNSZone Set-Alias Get-DNSRecord Get-DomainDNSRecord Set-Alias Get-NetDomain Get-Domain Set-Alias Get-NetDomainController Get-DomainController Set-Alias Get-NetForest Get-Forest Set-Alias Get-NetForestDomain Get-ForestDomain Set-Alias Get-NetForestCatalog Get-ForestGlobalCatalog Set-Alias Get-NetUser Get-DomainUser Set-Alias Get-UserEvent Get-DomainUserEvent Set-Alias Get-NetComputer Get-DomainComputer Set-Alias Get-ADObject Get-DomainObject Set-Alias Set-ADObject Set-DomainObject Set-Alias Get-ObjectAcl Get-DomainObjectAcl Set-Alias Add-ObjectAcl Add-DomainObjectAcl Set-Alias Invoke-ACLScanner Find-InterestingDomainAcl Set-Alias Get-GUIDMap Get-DomainGUIDMap Set-Alias Get-NetOU Get-DomainOU Set-Alias Get-NetSite Get-DomainSite Set-Alias Get-NetSubnet Get-DomainSubnet Set-Alias Get-NetGroup Get-DomainGroup Set-Alias Find-ManagedSecurityGroups Get-DomainManagedSecurityGroup Set-Alias Get-NetGroupMember Get-DomainGroupMember Set-Alias Get-NetFileServer Get-DomainFileServer Set-Alias Get-DFSshare Get-DomainDFSShare Set-Alias Get-NetGPO Get-DomainGPO Set-Alias Get-NetGPOGroup Get-DomainGPOLocalGroup Set-Alias Find-GPOLocation Get-DomainGPOUserLocalGroupMapping Set-Alias Find-GPOComputerAdmin Get-DomainGPOComputerLocalGroupMapping Set-Alias Get-LoggedOnLocal Get-RegLoggedOn Set-Alias Invoke-CheckLocalAdminAccess Test-AdminAccess Set-Alias Get-SiteName Get-NetComputerSiteName Set-Alias Get-Proxy Get-WMIRegProxy Set-Alias Get-LastLoggedOn Get-WMIRegLastLoggedOn Set-Alias Get-CachedRDPConnection Get-WMIRegCachedRDPConnection Set-Alias Get-RegistryMountedDrive Get-WMIRegMountedDrive Set-Alias Get-NetProcess Get-WMIProcess Set-Alias Invoke-ThreadedFunction New-ThreadedFunction Set-Alias Invoke-UserHunter Find-DomainUserLocation Set-Alias Invoke-ProcessHunter Find-DomainProcess Set-Alias Invoke-EventHunter Find-DomainUserEvent Set-Alias Invoke-ShareFinder Find-DomainShare Set-Alias Invoke-FileFinder Find-InterestingDomainShareFile Set-Alias Invoke-EnumerateLocalAdmin Find-DomainLocalGroupMember Set-Alias Get-NetDomainTrust Get-DomainTrust Set-Alias Get-NetForestTrust Get-ForestTrust Set-Alias Find-ForeignUser Get-DomainForeignUser Set-Alias Find-ForeignGroup Get-DomainForeignGroupMember Set-Alias Invoke-MapDomainTrust Get-DomainTrustMapping Set-Alias Get-DomainPolicy Get-DomainPolicyData 6f718906-bd52-4f23-b3d7-d6a3bc93b7e5 4104132150x0131356Microsoft-Windows-PowerShell/Operationalwin-dc-tcontreras-attack-range-677.attackrange.local7476'SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks } if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential } if ($PSBoundParameters['Raw']) { $SearcherArguments['Raw'] = $Raw } } PROCESS { # standard group names to ignore $ExcludeGroups = @('Users', 'Domain Users', 'Guests') Get-DomainGroup @SearcherArguments | Where-Object { $ExcludeGroups -notcontains $_.samaccountname } | ForEach-Object { $GroupName = $_.samAccountName $GroupDistinguishedName = $_.distinguishedname $GroupDomain = $GroupDistinguishedName.SubString($GroupDistinguishedName.IndexOf('DC=')) -replace 'DC=','' -replace ',','.' $_.member | ForEach-Object { # filter for foreign SIDs in the cn field for users in another domain, # or if the DN doesn't end with the proper DN for the queried domain $MemberDomain = $_.SubString($_.IndexOf('DC=')) -replace 'DC=','' -replace ',','.' if (($_ -match 'CN=S-1-5-21.*-.*') -or ($GroupDomain -ne $MemberDomain)) { $MemberDistinguishedName = $_ $MemberName = $_.Split(',')[0].split('=')[1] $ForeignGroupMember = New-Object PSObject $ForeignGroupMember | Add-Member Noteproperty 'GroupDomain' $GroupDomain $ForeignGroupMember | Add-Member Noteproperty 'GroupName' $GroupName $ForeignGroupMember | Add-Member Noteproperty 'GroupDistinguishedName' $GroupDistinguishedName $ForeignGroupMember | Add-Member Noteproperty 'MemberDomain' $MemberDomain $ForeignGroupMember | Add-Member Noteproperty 'MemberName' $MemberName $ForeignGroupMember | Add-Member Noteproperty 'MemberDistinguishedName' $MemberDistinguishedName $ForeignGroupMember.PSObject.TypeNames.Insert(0, 'PowerView.ForeignGroupMember') $ForeignGroupMember } } } } } function Get-DomainTrustMapping { <# .SYNOPSIS This function enumerates all trusts for the current domain and then enumerates all trusts for each domain it finds. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-Domain, Get-DomainTrust, Get-ForestTrust .DESCRIPTION This function will enumerate domain trust relationships for the current domain using a number of methods, and then enumerates all trusts for each found domain, recursively mapping all reachable trust relationships. By default, and LDAP search using the filter '(objectClass=trustedDomain)' is used- if any LDAP-appropriate parameters are specified LDAP is used as well. If the -NET flag is specified, the .NET method GetAllTrustRelationships() is used on the System.DirectoryServices.ActiveDirectory.Domain object. If the -API flag is specified, the Win32 API DsEnumerateDomainTrusts() call is used to enumerate instead. If any .PARAMETER API Switch. Use an API call (DsEnumerateDomainTrusts) to enumerate the trusts instead of the built-in LDAP method. .PARAMETER NET Switch. Use .NET queries to enumerate trusts instead of the default LDAP method. .PARAMETER LDAPFilter Specifies an LDAP query string that is used to filter Active Directory objects. .PARAMETER Properties Specifies the properties of the output object to retrieve from the server. .PARAMETER SearchBase The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local" Useful for OU queries. .PARAMETER Server Specifies an Active Directory server (domain controller) to bind to. .PARAMETER SearchScope Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree). .PARAMETER ResultPageSize Specifies the PageSize to set for the LDAP searcher object. .PARAMETER ServerTimeLimit Specifies the maximum amount of time the server spends searching. Default of 120 seconds. .PARAMETER Tombstone Switch. Specifies that the searcher should also return deleted/tombstoned objects. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to the target domain. .EXAMPLE Get-DomainTrustMapping | Export-CSV -NoTypeInformation trusts.csv Map all reachable domain trusts using .NET methods and output everything to a .csv file. .EXAMPLE Get-DomainTrustMapping -API | Export-CSV -NoTypeInformation trusts.csv Map all reachable domain trusts using Win32 API calls and output everything to a .csv file. .EXAMPLE Get-DomainTrustMapping -NET | Export-CSV -NoTypeInformation trusts.csv Map all reachable domain trusts using .NET methods and output everything to a .csv file. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainTrustMapping -Server 'PRIMARY.testlab.local' | Export-CSV -NoTypeInformation trusts.csv Map all reachable domain trusts using LDAP, binding to the PRIMARY.testlab.local server for queries using the specified alternate credentials, and output everything to a .csv file. .OUTPUTS PowerView.DomainTrust.LDAP Custom PSObject with translated domain LDAP trust result fields (default). PowerView.DomainTrust.NET A TrustRelationshipInformationCollection returned when using .NET methods. PowerView.DomainTrust.API Custom PSObject with translated domain API trust result fields. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.DomainTrust.NET')] [OutputType('PowerView.DomainTrust.LDAP')] [OutputType('PowerView.DomainTrust.API')] [CmdletBinding(DefaultParameterSetName = 'LDAP')] Param( [Parameter(ParameterSetName = 'API')] [Switch] $API, [Parameter(ParameterSetName = 'NET')] [Switch] $NET, [Parameter(ParameterSetName = 'LDAP')] [ValidateNotNullOrEmpty()] [Alias('Filter')] [String] $LDAPFilter, [Parameter(ParameterSetName = 'LDAP')] [ValidateNotNullOrEmpty()] [String[]] $Properties, [Parameter(ParameterSetName = 'LDAP')] [ValidateNotNullOrEmpty()] [Alias('ADSPath')] [String] $SearchBase, [Parameter(ParameterSetName = 'LDAP')] [Parameter(ParameterSetName = 'API')] [ValidateNotNullOrEmpty()] [Alias('DomainController')] [String] $Server, [Parameter(ParameterSetName = 'LDAP')] [ValidateSet('Base', 'OneLevel', 'Subtree')] [String] $SearchScope = 'Subtree', [Parameter(ParameterSetName = 'LDAP')] [ValidateRange(1, 10000)] [Int] $ResultPageSize = 200, [Parameter(ParameterSetName = 'LDAP')] [ValidateRange(1, 10000)] [Int] $ServerTimeLimit, [Parameter(ParameterSetName = 'LDAP')] [Switch] $Tombstone, [Parameter(ParameterSetName = 'LDAP')] [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) # keep track of domains seen so we don't hit infinite recursion $SeenDomains = @{} # our domain status tracker $Domains = New-Object System.Collections.Stack $DomainTrustArguments = @{} if ($PSBoundParameters['API']) { $DomainTrustArguments['API'] = $API } if ($PSBoundParameters['NET']) { $DomainTrustArguments['NET'] = $NET } if ($PSBoundParameters['LDAPFilter']) { $DomainTrustArguments['LDAPFilter'] = $LDAPFilter } if ($PSBoundParameters['Properties']) { $DomainTrustArguments['Properties'] = $Properties } if ($PSBoundParameters['SearchBase']) { $DomainTrustArguments['SearchBase'] = $SearchBase } if ($PSBoundParameters['Server']) { $DomainTrustArguments['Server'] = $Server } if ($PSBoundParameters['SearchScope']) { $DomainTrustArguments['SearchScope'] = $SearchScope } if ($PSBoundParameters['ResultPageSize']) { $DomainTrustArguments['ResultPageSize'] = $ResultPageSize } if ($PSBoundParameters['ServerTimeLimit']) { $DomainTrustArguments['ServerTimeLimit'] = $ServerTimeLimit } if ($PSBoundParameters['Tombstone']) { $DomainTrustArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $DomainTrustArguments['Credential'] = $Credential } # get the current domain and push it onto the stack if ($PSBoundParameters['Credential']) { $CurrentDomain = (Get-Domain -Credential $Credential).Name } else { $CurrentDomain = (Get-Domain).Name } $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 trusts for this domain $DomainTrustArguments['Domain'] = $Domain $Trusts = Get-DomainTrust @DomainTrustArguments if ($Trusts -isnot [System.Array]) { $Trusts = @($Trusts) } # get any forest trusts, if they exist if ($PsCmdlet.ParameterSetName -eq 'NET') { $ForestTrustArguments = @{} if ($PSBoundParameters['Forest']) { $ForestTrustArguments['Forest'] = $Forest } if ($PSBoundParameters['Credential']) { $ForestTrustArguments['Credential'] = $Credential } $Trusts += Get-ForestTrust @ForestTrustArguments } if ($Trusts) { if ($Trusts -isnot [System.Array]) { $Trusts = @($Trusts) 6f718906-bd52-4f23-b3d7-d6a3bc93b7e5 4104132150x0131354Microsoft-Windows-PowerShell/Operationalwin-dc-tcontreras-attack-range-677.attackrange.local7276rops.trustdirection) { 0 { 'Disabled' } 1 { 'Inbound' } 2 { 'Outbound' } 3 { 'Bidirectional' } } $TrustType = Switch ($Props.trusttype) { 1 { 'WINDOWS_NON_ACTIVE_DIRECTORY' } 2 { 'WINDOWS_ACTIVE_DIRECTORY' } 3 { 'MIT' } } $Distinguishedname = $Props.distinguishedname[0] $SourceNameIndex = $Distinguishedname.IndexOf('DC=') if ($SourceNameIndex) { $SourceDomain = $($Distinguishedname.SubString($SourceNameIndex)) -replace 'DC=','' -replace ',','.' } else { $SourceDomain = "" } $TargetNameIndex = $Distinguishedname.IndexOf(',CN=System') if ($SourceNameIndex) { $TargetDomain = $Distinguishedname.SubString(3, $TargetNameIndex-3) } else { $TargetDomain = "" } $ObjectGuid = New-Object Guid @(,$Props.objectguid[0]) $TargetSID = (New-Object System.Security.Principal.SecurityIdentifier($Props.securityidentifier[0],0)).Value $DomainTrust | Add-Member Noteproperty 'SourceName' $SourceDomain $DomainTrust | Add-Member Noteproperty 'TargetName' $Props.name[0] # $DomainTrust | Add-Member Noteproperty 'TargetGuid' "{$ObjectGuid}" $DomainTrust | Add-Member Noteproperty 'TrustType' $TrustType $DomainTrust | Add-Member Noteproperty 'TrustAttributes' $($TrustAttrib -join ',') $DomainTrust | Add-Member Noteproperty 'TrustDirection' "$Direction" $DomainTrust | Add-Member Noteproperty 'WhenCreated' $Props.whencreated[0] $DomainTrust | Add-Member Noteproperty 'WhenChanged' $Props.whenchanged[0] $DomainTrust.PSObject.TypeNames.Insert(0, 'PowerView.DomainTrust.LDAP') $DomainTrust } if ($Results) { try { $Results.dispose() } catch { Write-Verbose "[Get-DomainTrust] Error disposing of the Results object: $_" } } $TrustSearcher.dispose() } } elseif ($PsCmdlet.ParameterSetName -eq 'API') { # if we're searching for domain trusts through Win32 API functions if ($PSBoundParameters['Server']) { $TargetDC = $Server } elseif ($Domain -and $Domain.Trim() -ne '') { $TargetDC = $Domain } else { # see https://msdn.microsoft.com/en-us/library/ms675976(v=vs.85).aspx for default NULL behavior $TargetDC = $Null } # arguments for DsEnumerateDomainTrusts $PtrInfo = [IntPtr]::Zero # 63 = DS_DOMAIN_IN_FOREST + DS_DOMAIN_DIRECT_OUTBOUND + DS_DOMAIN_TREE_ROOT + DS_DOMAIN_PRIMARY + DS_DOMAIN_NATIVE_MODE + DS_DOMAIN_DIRECT_INBOUND $Flags = 63 $DomainCount = 0 # get the trust information from the target server $Result = $Netapi32::DsEnumerateDomainTrusts($TargetDC, $Flags, [ref]$PtrInfo, [ref]$DomainCount) # Locate the offset of the initial intPtr $Offset = $PtrInfo.ToInt64() # 0 = success if (($Result -eq 0) -and ($Offset -gt 0)) { # Work out how much to increment the pointer by finding out the size of the structure $Increment = $DS_DOMAIN_TRUSTS::GetSize() # parse all the result structures for ($i = 0; ($i -lt $DomainCount); $i++) { # create a new int ptr at the given offset and cast the pointer as our result structure $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset $Info = $NewIntPtr -as $DS_DOMAIN_TRUSTS $Offset = $NewIntPtr.ToInt64() $Offset += $Increment $SidString = '' $Result = $Advapi32::ConvertSidToStringSid($Info.DomainSid, [ref]$SidString);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error() if ($Result -eq 0) { Write-Verbose "[Get-DomainTrust] Error: $(([ComponentModel.Win32Exception] $LastError).Message)" } else { $DomainTrust = New-Object PSObject $DomainTrust | Add-Member Noteproperty 'SourceName' $SourceDomain $DomainTrust | Add-Member Noteproperty 'TargetName' $Info.DnsDomainName $DomainTrust | Add-Member Noteproperty 'TargetNetbiosName' $Info.NetbiosDomainName $DomainTrust | Add-Member Noteproperty 'Flags' $Info.Flags $DomainTrust | Add-Member Noteproperty 'ParentIndex' $Info.ParentIndex $DomainTrust | Add-Member Noteproperty 'TrustType' $Info.TrustType $DomainTrust | Add-Member Noteproperty 'TrustAttributes' $Info.TrustAttributes $DomainTrust | Add-Member Noteproperty 'TargetSid' $SidString $DomainTrust | Add-Member Noteproperty 'TargetGuid' $Info.DomainGuid $DomainTrust.PSObject.TypeNames.Insert(0, 'PowerView.DomainTrust.API') $DomainTrust } } # free up the result buffer $Null = $Netapi32::NetApiBufferFree($PtrInfo) } else { Write-Verbose "[Get-DomainTrust] Error: $(([ComponentModel.Win32Exception] $Result).Message)" } } else { # if we're searching for domain trusts through .NET methods $FoundDomain = Get-Domain @NetSearcherArguments if ($FoundDomain) { $FoundDomain.GetAllTrustRelationships() | ForEach-Object { $_.PSObject.TypeNames.Insert(0, 'PowerView.DomainTrust.NET') $_ } } } } } function Get-ForestTrust { <# .SYNOPSIS Return all forest trusts for the current forest or a specified forest. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-Forest .DESCRIPTION This function will enumerate domain trust relationships for the current (or a remote) forest using number of method using the .NET method GetAllTrustRelationships() on a System.DirectoryServices.ActiveDirectory.Forest returned by Get-Forest. .PARAMETER Forest Specifies the forest to query for trusts, defaults to the current forest. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to the target domain. .EXAMPLE Get-ForestTrust Return current forest trusts. .EXAMPLE Get-ForestTrust -Forest "external.local" Return trusts for the "external.local" forest. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-ForestTrust -Forest "external.local" -Credential $Cred Return trusts for the "external.local" forest using the specified alternate credenitals. .OUTPUTS PowerView.DomainTrust.NET A TrustRelationshipInformationCollection returned when using .NET methods (default). #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.ForestTrust.NET')] [CmdletBinding()] Param( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('Name')] [ValidateNotNullOrEmpty()] [String] $Forest, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) PROCESS { $NetForestArguments = @{} if ($PSBoundParameters['Forest']) { $NetForestArguments['Forest'] = $Forest } if ($PSBoundParameters['Credential']) { $NetForestArguments['Credential'] = $Credential } $FoundForest = Get-Forest @NetForestArguments if ($FoundForest) { $FoundForest.GetAllTrustRelationships() | ForEach-Object { $_.PSObject.TypeNames.Insert(0, 'PowerView.ForestTrust.NET') $_ } } } } function Get-DomainForeignUser { <# .SYNOPSIS Enumerates users who are in groups outside of the user's domain. This is a domain's "outgoing" access. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-Domain, Get-DomainUser .DESCRIPTION Uses Get-DomainUser to enumerate all users for the current (or target) domain, then calculates the given user's domain name based on the user's distinguishedName. This domain name is compared to the queried domain, and the user object is output if they differ. .PARAMETER Domain Specifies the domain to use for the query, defaults to the current domain. .PARAMETER LDAPFilter Specifies an LDAP query string that is used to filter Active Directory objects. .PARAMETER Properties Specifies the properties of the output object to retrieve from the server. .PARAMETER SearchBase The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local" Useful for OU queries. .PARAMETER Server Specifies an Active Directory server (domain controller) to bind to. .PARAMETER SearchScope Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree). .PARAMETER ResultPageSize Specifies the PageSize to set for the LDAP searcher object. .PARAMETER ServerTimeLimit Specifies the maximum am6f718906-bd52-4f23-b3d7-d6a3bc93b7e5 4104132150x0130556Microsoft-Windows-PowerShell/Operationalwin-dc-tcontreras-attack-range-677.attackrange.local4444KEY_ONLY = 2097152 DONT_REQ_PREAUTH = 4194304 PASSWORD_EXPIRED = 8388608 TRUSTED_TO_AUTH_FOR_DELEGATION = 16777216 PARTIAL_SECRETS_ACCOUNT = 67108864 } -Bitfield # enum used by $WTS_SESSION_INFO_1 below $WTSConnectState = psenum $Mod WTS_CONNECTSTATE_CLASS UInt16 @{ Active = 0 Connected = 1 ConnectQuery = 2 Shadow = 3 Disconnected = 4 Idle = 5 Listen = 6 Reset = 7 Down = 8 Init = 9 } # the WTSEnumerateSessionsEx result structure $WTS_SESSION_INFO_1 = struct $Mod PowerView.RDPSessionInfo @{ ExecEnvId = field 0 UInt32 State = field 1 $WTSConnectState SessionId = field 2 UInt32 pSessionName = field 3 String -MarshalAs @('LPWStr') pHostName = field 4 String -MarshalAs @('LPWStr') pUserName = field 5 String -MarshalAs @('LPWStr') pDomainName = field 6 String -MarshalAs @('LPWStr') pFarmName = field 7 String -MarshalAs @('LPWStr') } # the particular WTSQuerySessionInformation result structure $WTS_CLIENT_ADDRESS = struct $mod WTS_CLIENT_ADDRESS @{ AddressFamily = field 0 UInt32 Address = field 1 Byte[] -MarshalAs @('ByValArray', 20) } # the NetShareEnum result structure $SHARE_INFO_1 = struct $Mod PowerView.ShareInfo @{ Name = field 0 String -MarshalAs @('LPWStr') Type = field 1 UInt32 Remark = field 2 String -MarshalAs @('LPWStr') } # the NetWkstaUserEnum result structure $WKSTA_USER_INFO_1 = struct $Mod PowerView.LoggedOnUserInfo @{ UserName = field 0 String -MarshalAs @('LPWStr') LogonDomain = field 1 String -MarshalAs @('LPWStr') AuthDomains = field 2 String -MarshalAs @('LPWStr') LogonServer = field 3 String -MarshalAs @('LPWStr') } # the NetSessionEnum result structure $SESSION_INFO_10 = struct $Mod PowerView.SessionInfo @{ CName = field 0 String -MarshalAs @('LPWStr') UserName = field 1 String -MarshalAs @('LPWStr') Time = field 2 UInt32 IdleTime = field 3 UInt32 } # enum used by $LOCALGROUP_MEMBERS_INFO_2 below $SID_NAME_USE = psenum $Mod SID_NAME_USE UInt16 @{ SidTypeUser = 1 SidTypeGroup = 2 SidTypeDomain = 3 SidTypeAlias = 4 SidTypeWellKnownGroup = 5 SidTypeDeletedAccount = 6 SidTypeInvalid = 7 SidTypeUnknown = 8 SidTypeComputer = 9 } # the NetLocalGroupEnum result structure $LOCALGROUP_INFO_1 = struct $Mod LOCALGROUP_INFO_1 @{ lgrpi1_name = field 0 String -MarshalAs @('LPWStr') lgrpi1_comment = field 1 String -MarshalAs @('LPWStr') } # the NetLocalGroupGetMembers result structure $LOCALGROUP_MEMBERS_INFO_2 = struct $Mod LOCALGROUP_MEMBERS_INFO_2 @{ lgrmi2_sid = field 0 IntPtr lgrmi2_sidusage = field 1 $SID_NAME_USE lgrmi2_domainandname = field 2 String -MarshalAs @('LPWStr') } # enums used in DS_DOMAIN_TRUSTS $DsDomainFlag = psenum $Mod DsDomain.Flags UInt32 @{ IN_FOREST = 1 DIRECT_OUTBOUND = 2 TREE_ROOT = 4 PRIMARY = 8 NATIVE_MODE = 16 DIRECT_INBOUND = 32 } -Bitfield $DsDomainTrustType = psenum $Mod DsDomain.TrustType UInt32 @{ DOWNLEVEL = 1 UPLEVEL = 2 MIT = 3 DCE = 4 } $DsDomainTrustAttributes = psenum $Mod DsDomain.TrustAttributes UInt32 @{ NON_TRANSITIVE = 1 UPLEVEL_ONLY = 2 FILTER_SIDS = 4 FOREST_TRANSITIVE = 8 CROSS_ORGANIZATION = 16 WITHIN_FOREST = 32 TREAT_AS_EXTERNAL = 64 } # the DsEnumerateDomainTrusts result structure $DS_DOMAIN_TRUSTS = struct $Mod DS_DOMAIN_TRUSTS @{ NetbiosDomainName = field 0 String -MarshalAs @('LPWStr') DnsDomainName = field 1 String -MarshalAs @('LPWStr') Flags = field 2 $DsDomainFlag ParentIndex = field 3 UInt32 TrustType = field 4 $DsDomainTrustType TrustAttributes = field 5 $DsDomainTrustAttributes DomainSid = field 6 IntPtr DomainGuid = field 7 Guid } # used by WNetAddConnection2W $NETRESOURCEW = struct $Mod NETRESOURCEW @{ dwScope = field 0 UInt32 dwType = field 1 UInt32 dwDisplayType = field 2 UInt32 dwUsage = field 3 UInt32 lpLocalName = field 4 String -MarshalAs @('LPWStr') lpRemoteName = field 5 String -MarshalAs @('LPWStr') lpComment = field 6 String -MarshalAs @('LPWStr') lpProvider = field 7 String -MarshalAs @('LPWStr') } # all of the Win32 API functions we need $FunctionDefinitions = @( (func netapi32 NetShareEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())), (func netapi32 NetWkstaUserEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())), (func netapi32 NetSessionEnum ([Int]) @([String], [String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())), (func netapi32 NetLocalGroupEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())), (func netapi32 NetLocalGroupGetMembers ([Int]) @([String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())), (func netapi32 DsGetSiteName ([Int]) @([String], [IntPtr].MakeByRefType())), (func netapi32 DsEnumerateDomainTrusts ([Int]) @([String], [UInt32], [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType())), (func netapi32 NetApiBufferFree ([Int]) @([IntPtr])), (func advapi32 ConvertSidToStringSid ([Int]) @([IntPtr], [String].MakeByRefType()) -SetLastError), (func advapi32 OpenSCManagerW ([IntPtr]) @([String], [String], [Int]) -SetLastError), (func advapi32 CloseServiceHandle ([Int]) @([IntPtr])), (func advapi32 LogonUser ([Bool]) @([String], [String], [String], [UInt32], [UInt32], [IntPtr].MakeByRefType()) -SetLastError), (func advapi32 ImpersonateLoggedOnUser ([Bool]) @([IntPtr]) -SetLastError), (func advapi32 RevertToSelf ([Bool]) @() -SetLastError), (func wtsapi32 WTSOpenServerEx ([IntPtr]) @([String])), (func wtsapi32 WTSEnumerateSessionsEx ([Int]) @([IntPtr], [Int32].MakeByRefType(), [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType()) -SetLastError), (func wtsapi32 WTSQuerySessionInformation ([Int]) @([IntPtr], [Int], [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType()) -SetLastError), (func wtsapi32 WTSFreeMemoryEx ([Int]) @([Int32], [IntPtr], [Int32])), (func wtsapi32 WTSFreeMemory ([Int]) @([IntPtr])), (func wtsapi32 WTSCloseServer ([Int]) @([IntPtr])), (func Mpr WNetAddConnection2W ([Int]) @($NETRESOURCEW, [String], [String], [UInt32])), (func Mpr WNetCancelConnection2 ([Int]) @([String], [Int], [Bool])), (func kernel32 CloseHandle ([Bool]) @([IntPtr]) -SetLastError) ) $Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Win32' $Netapi32 = $Types['netapi32'] $Advapi32 = $Types['advapi32'] $Wtsapi32 = $Types['wtsapi32'] $Mpr = $Types['Mpr'] $Kernel32 = $Types['kernel32'] Set-Alias Get-IPAddress Resolve-IPAddress Set-Alias Convert-NameToSid ConvertTo-SID Set-Alias Convert-SidToName ConvertFrom-SID Set-Alias Request-SPNTicket Get-DomainSPNTicket Set-Alias Get-DNSZone Get-DomainDNSZone Set-Alias Get-DNSRecord Get-DomainDNSRecord Set-Alias Get-NetDomain Get-Domain Set-Alias Get-NetDomainController Get-DomainController Set-Alias Get-NetForest Get-Forest Set-Alias Get-NetForestDomain Get-ForestDomain Set-Alias Get-NetForestCatalog Get-ForestGlobalCatalog Set-Alias Get-NetUser Get-DomainUser Set-Alias Get-UserEvent Get-DomainUserEvent Set-Alias Get-NetComputer Get-DomainComputer Set-Alias Get-ADObject Get-DomainObject Set-Alias Set-ADObject Set-DomainObject Set-Alias Get-ObjectAcl Get-DomainObjectAcl Set-Alias Add-ObjectAcl Add-DomainObjectAcl Set-Alias Invoke-ACLScanner Find-InterestingDomainAcl Set-Alias Get-GUIDMap Get-DomainGUIDMap Set-Alias Get-NetOU Get-DomainOU Set-Alias Get-NetSite Get-DomainSite Set-Alias Get-NetSubnet Get-DomainSubnet Set-Alias Get-NetGroup Get-DomainGroup Set-Alias Find-ManagedSecurityGroups Get-DomainManagedSecurityGroup Set-Alias Get-NetGroupMember Get-DomainGroupMember Set-Alias Get-NetFileServer Get-DomainFileServer Set-Alias Get-DFSshare Get-DomainDFSShare Set-Alias Get-NetGPO Get-DomainGPO Set-Alias Get-NetGPOGroup Get-DomainGPOLocalGroup Set-Alias Find-GPOLocation Get-DomainGPOUserLocalGroupMapping Set-Alias Find-GPOComputerAdmin Get-DomainGPOComputerLocalGroupMapping Set-Alias Get-LoggedOnLocal Get-RegLoggedOn Set-Alias Invoke-CheckLocalAdminAccess Test-AdminAccess Set-Alias Get-SiteName Get-NetComputerSiteName Set-Alias Get-Proxy Get-WMIRegProxy Set-Alias Get-LastLoggedOn Get-WMIRegLastLoggedOn Set-Alias Get-CachedRDPConnection Get-WMIRegCachedRDPConnection Set-Alias Get-RegistryMountedDrive Get-WMIRegMountedDrive Set-Alias Get-NetProcess Get-WMIProcess Set-Alias Invoke-ThreadedFunction New-ThreadedFunction Set-Alias Invoke-UserHunter Find-DomainUserLocation Set-Alias Invoke-ProcessHunter Find-DomainProcess Set-Alias Invoke-EventHunter Find-DomainUserEvent Set-Alias Invoke-ShareFinder Find-DomainShare Set-Alias Invoke-FileFinder Find-InterestingDomainShareFile Set-Alias Invoke-EnumerateLocalAdmin Find-DomainLocalGroupMember Set-Alias Get-NetDomainTrust Get-DomainTrust Set-Alias Get-NetForestTrust Get-ForestTrust Set-Alias Find-ForeignUser Get-DomainForeignUser Set-Alias Find-ForeignGroup Get-DomainForeignGroupMember Set-Alias Invoke-MapDomainTrust Get-DomainTrustMapping Set-Alias Get-DomainPolicy Get-DomainPolicyData a643b580-b5af-4819-96e0-1b706cd7fc22C:\Users\Administrator\AppData\Local\Temp\PowerView.ps1 4104132150x0130555Microsoft-Windows-PowerShell/Operationalwin-dc-tcontreras-attack-range-677.attackrange.local4344om PSObject with translated group member property fields. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.ForeignGroupMember')] [CmdletBinding()] Param( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('Name')] [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, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $SearcherArguments = @{} $SearcherArguments['LDAPFilter'] = '(member=*)' 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 } if ($PSBoundParameters['Raw']) { $SearcherArguments['Raw'] = $Raw } } PROCESS { # standard group names to ignore $ExcludeGroups = @('Users', 'Domain Users', 'Guests') Get-DomainGroup @SearcherArguments | Where-Object { $ExcludeGroups -notcontains $_.samaccountname } | ForEach-Object { $GroupName = $_.samAccountName $GroupDistinguishedName = $_.distinguishedname $GroupDomain = $GroupDistinguishedName.SubString($GroupDistinguishedName.IndexOf('DC=')) -replace 'DC=','' -replace ',','.' $_.member | ForEach-Object { # filter for foreign SIDs in the cn field for users in another domain, # or if the DN doesn't end with the proper DN for the queried domain $MemberDomain = $_.SubString($_.IndexOf('DC=')) -replace 'DC=','' -replace ',','.' if (($_ -match 'CN=S-1-5-21.*-.*') -or ($GroupDomain -ne $MemberDomain)) { $MemberDistinguishedName = $_ $MemberName = $_.Split(',')[0].split('=')[1] $ForeignGroupMember = New-Object PSObject $ForeignGroupMember | Add-Member Noteproperty 'GroupDomain' $GroupDomain $ForeignGroupMember | Add-Member Noteproperty 'GroupName' $GroupName $ForeignGroupMember | Add-Member Noteproperty 'GroupDistinguishedName' $GroupDistinguishedName $ForeignGroupMember | Add-Member Noteproperty 'MemberDomain' $MemberDomain $ForeignGroupMember | Add-Member Noteproperty 'MemberName' $MemberName $ForeignGroupMember | Add-Member Noteproperty 'MemberDistinguishedName' $MemberDistinguishedName $ForeignGroupMember.PSObject.TypeNames.Insert(0, 'PowerView.ForeignGroupMember') $ForeignGroupMember } } } } } function Get-DomainTrustMapping { <# .SYNOPSIS This function enumerates all trusts for the current domain and then enumerates all trusts for each domain it finds. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-Domain, Get-DomainTrust, Get-ForestTrust .DESCRIPTION This function will enumerate domain trust relationships for the current domain using a number of methods, and then enumerates all trusts for each found domain, recursively mapping all reachable trust relationships. By default, and LDAP search using the filter '(objectClass=trustedDomain)' is used- if any LDAP-appropriate parameters are specified LDAP is used as well. If the -NET flag is specified, the .NET method GetAllTrustRelationships() is used on the System.DirectoryServices.ActiveDirectory.Domain object. If the -API flag is specified, the Win32 API DsEnumerateDomainTrusts() call is used to enumerate instead. If any .PARAMETER API Switch. Use an API call (DsEnumerateDomainTrusts) to enumerate the trusts instead of the built-in LDAP method. .PARAMETER NET Switch. Use .NET queries to enumerate trusts instead of the default LDAP method. .PARAMETER LDAPFilter Specifies an LDAP query string that is used to filter Active Directory objects. .PARAMETER Properties Specifies the properties of the output object to retrieve from the server. .PARAMETER SearchBase The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local" Useful for OU queries. .PARAMETER Server Specifies an Active Directory server (domain controller) to bind to. .PARAMETER SearchScope Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree). .PARAMETER ResultPageSize Specifies the PageSize to set for the LDAP searcher object. .PARAMETER ServerTimeLimit Specifies the maximum amount of time the server spends searching. Default of 120 seconds. .PARAMETER Tombstone Switch. Specifies that the searcher should also return deleted/tombstoned objects. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to the target domain. .EXAMPLE Get-DomainTrustMapping | Export-CSV -NoTypeInformation trusts.csv Map all reachable domain trusts using .NET methods and output everything to a .csv file. .EXAMPLE Get-DomainTrustMapping -API | Export-CSV -NoTypeInformation trusts.csv Map all reachable domain trusts using Win32 API calls and output everything to a .csv file. .EXAMPLE Get-DomainTrustMapping -NET | Export-CSV -NoTypeInformation trusts.csv Map all reachable domain trusts using .NET methods and output everything to a .csv file. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainTrustMapping -Server 'PRIMARY.testlab.local' | Export-CSV -NoTypeInformation trusts.csv Map all reachable domain trusts using LDAP, binding to the PRIMARY.testlab.local server for queries using the specified alternate credentials, and output everything to a .csv file. .OUTPUTS PowerView.DomainTrust.LDAP Custom PSObject with translated domain LDAP trust result fields (default). PowerView.DomainTrust.NET A TrustRelationshipInformationCollection returned when using .NET methods. PowerView.DomainTrust.API Custom PSObject with translated domain API trust result fields. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.DomainTrust.NET')] [OutputType('PowerView.DomainTrust.LDAP')] [OutputType('PowerView.DomainTrust.API')] [CmdletBinding(DefaultParameterSetName = 'LDAP')] Param( [Parameter(ParameterSetName = 'API')] [Switch] $API, [Parameter(ParameterSetName = 'NET')] [Switch] $NET, [Parameter(ParameterSetName = 'LDAP')] [ValidateNotNullOrEmpty()] [Alias('Filter')] [String] $LDAPFilter, [Parameter(ParameterSetName = 'LDAP')] [ValidateNotNullOrEmpty()] [String[]] $Properties, [Parameter(ParameterSetName = 'LDAP')] [ValidateNotNullOrEmpty()] [Alias('ADSPath')] [String] $SearchBase, [Parameter(ParameterSetName = 'LDAP')] [Parameter(ParameterSetName = 'API')] [ValidateNotNullOrEmpty()] [Alias('DomainController')] [String] $Server, [Parameter(ParameterSetName = 'LDAP')] [ValidateSet('Base', 'OneLevel', 'Subtree')] [String] $SearchScope = 'Subtree', [Parameter(ParameterSetName = 'LDAP')] [ValidateRange(1, 10000)] [Int] $ResultPageSize = 200, [Parameter(ParameterSetName = 'LDAP')] [ValidateRange(1, 10000)] [Int] $ServerTimeLimit, [Parameter(ParameterSetName = 'LDAP')] [Switch] $Tombstone, [Parameter(ParameterSetName = 'LDAP')] [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) # keep track of domains seen so we don't hit infinite recursion $SeenDomains = @{} # our domain status tracker $Domains = New-Object System.Collections.Stack $DomainTrustArguments = @{} if ($PSBoundParameters['API']) { $DomainTrustArguments['API'] = $API } if ($PSBoundParameters['NET']) { $DomainTrustArguments['NET'] = $NET } if ($PSBoundParameters['LDAPFilter']) { $DomainTrustArguments['LDAPFilter'] = $LDAPFilter } if ($PSBoundParameters['Properties']) { $DomainTrustArguments['Properties'] = $Properties } if ($PSBoundParameters['SearchBase']) { $DomainTrustArguments['SearchBase'] = $SearchBase } if ($PSBoundParameters['Server']) { $DomainTrustArguments['Server'] = $Server } if ($PSBoundParameters['SearchScope']) { $DomainTrustArguments['SearchScope'] = $SearchScope } if ($PSBoundParameters['ResultPageSize']) { $DomainTrustArguments['ResultPageSize'] = $ResultPageSize } if ($PSBoundParameters['ServerTimeLimit']) { $DomainTrustArguments['ServerTimeLimit'] = $ServerTimeLimit } if ($PSBoundParameters['Tombstone']) { $DomainTrustArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $DomainTrustArguments['Credential'] = $Credential } # get the current domain and push it onto the stack if ($PSBoundParameters['Credential']) { $CurrentDomain = (Get-Domain -Credential $Credential).Name } else { $CurrentDomain = (Get-Domain).Name } $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 trusts for this domain $DomainTrustArguments['Domain'] = $Domain $Trusts = Get-DomainTrust @DomainTrustArguments if ($Trusts -isnot [System.Array]) { $Trusts = @($Trusts) } # get any forest trusts, if they exist if ($PsCmdlet.ParameterSetName -eq 'NET') { $ForestTrustArguments = @{} if ($PSBoundParameters['Forest']) { $ForestTrustArguments['Forest'] = $Forest } if ($PSBoundParameters['Credential']) { $ForestTrustArguments['Credential'] = $Credential } $Trusts += Get-ForestTrust @ForestTrustArguments } if ($Trusts) { if ($Trusts -isnot [System.Array]) { $Trusts = @($Trusts) } # enumerate each trust found ForEach ($Trust in $Trusts) { if ($Trust.SourceName -and $Trust.TargetName) { # make sure we process the target $Null = $Domains.Push($Trust.TargetName) $Trust } } } } catch { Write-Verbose "[Get-DomainTrustMapping] Error: $_" } } } } function Get-GPODelegation { <# .SYNOPSIS Finds users with write permissions on GPO objects which may allow privilege escalation within the domain. Author: Itamar Mizrahi (@MrAnde7son) License: BSD 3-Clause Required Dependencies: None .PARAMETER GPOName The GPO display name to query for, wildcards accepted. .PARAMETER PageSize Specifies the PageSize to set for the LDAP searcher object. .EXAMPLE Get-GPODelegation Returns all GPO delegations in current forest. .EXAMPLE Get-GPODelegation -GPOName Returns all GPO delegations on a given GPO. #> [CmdletBinding()] Param ( [String] $GPOName = '*', [ValidateRange(1,10000)] [Int] $PageSize = 200 ) $Exclusions = @('SYSTEM','Domain Admins','Enterprise Admins') $Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest() $DomainList = @($Forest.Domains) $Domains = $DomainList | foreach { $_.GetDirectoryEntry() } foreach ($Domain in $Domains) { $Filter = "(&(objectCategory=groupPolicyContainer)(displayname=$GPOName))" $Searcher = New-Object System.DirectoryServices.DirectorySearcher $Searcher.SearchRoot = $Domain $Searcher.Filter = $Filter $Searcher.PageSize = $PageSize $Searcher.SearchScope = "Subtree" $listGPO = $Searcher.FindAll() foreach ($gpo in $listGPO){ $ACL = ([ADSI]$gpo.path).ObjectSecurity.Access | ? {$_.ActiveDirectoryRights -match "Write" -and $_.AccessControlType -eq "Allow" -and $Exclusions -notcontains $_.IdentityReference.toString().split("\")[1] -and $_.IdentityReference -ne "CREATOR OWNER"} if ($ACL -ne $null){ $GpoACL = New-Object psobject $GpoACL | Add-Member Noteproperty 'ADSPath' $gpo.Properties.adspath $GpoACL | Add-Member Noteproperty 'GPODisplayName' $gpo.Properties.displayname $GpoACL | Add-Member Noteproperty 'IdentityReference' $ACL.IdentityReference $GpoACL | Add-Member Noteproperty 'ActiveDirectoryRights' $ACL.ActiveDirectoryRights $GpoACL } } } } ######################################################## # # Expose the Win32API functions and datastructures below # using PSReflect. # Warning: Once these are executed, they are baked in # and can't be changed while the script is running! # ######################################################## $Mod = New-InMemoryModule -ModuleName Win32 # [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPositionalParameters', Scope='Function', Target='psenum')] # used to parse the 'samAccountType' property for users/computers/groups $SamAccountTypeEnum = psenum $Mod PowerView.SamAccountTypeEnum UInt32 @{ DOMAIN_OBJECT = '0x00000000' GROUP_OBJECT = '0x10000000' NON_SECURITY_GROUP_OBJECT = '0x10000001' ALIAS_OBJECT = '0x20000000' NON_SECURITY_ALIAS_OBJECT = '0x20000001' USER_OBJECT = '0x30000000' MACHINE_ACCOUNT = '0x30000001' TRUST_ACCOUNT = '0x30000002' APP_BASIC_GROUP = '0x40000000' APP_QUERY_GROUP = '0x40000001' ACCOUNT_TYPE_MAX = '0x7fffffff' } # used to parse the 'grouptype' property for groups $GroupTypeEnum = psenum $Mod PowerView.GroupTypeEnum UInt32 @{ CREATED_BY_SYSTEM = '0x00000001' GLOBAL_SCOPE = '0x00000002' DOMAIN_LOCAL_SCOPE = '0x00000004' UNIVERSAL_SCOPE = '0x00000008' APP_BASIC = '0x00000010' APP_QUERY = '0x00000020' SECURITY = '0x80000000' } -Bitfield # used to parse the 'userAccountControl' property for users/groups $UACEnum = psenum $Mod PowerView.UACEnum UInt32 @{ SCRIPT = 1 ACCOUNTDISABLE = 2 HOMEDIR_REQUIRED = 8 LOCKOUT = 16 PASSWD_NOTREQD = 32 PASSWD_CANT_CHANGE = 64 ENCRYPTED_TEXT_PWD_ALLOWED = 128 TEMP_DUPLICATE_ACCOUNT = 256 NORMAL_ACCOUNT = 512 INTERDOMAIN_TRUST_ACCOUNT = 2048 WORKSTATION_TRUST_ACCOUNT = 4096 SERVER_TRUST_ACCOUNT = 8192 DONT_EXPIRE_PASSWORD = 65536 MNS_LOGON_ACCOUNT = 131072 SMARTCARD_REQUIRED = 262144 TRUSTED_FOR_DELEGATION = 524288 NOT_DELEGATED = 1048576 USE_DES_a643b580-b5af-4819-96e0-1b706cd7fc22C:\Users\Administrator\AppData\Local\Temp\PowerView.ps1 4104132150x0130554Microsoft-Windows-PowerShell/Operationalwin-dc-tcontreras-attack-range-677.attackrange.local4244 $SourceNameIndex = $Distinguishedname.IndexOf('DC=') if ($SourceNameIndex) { $SourceDomain = $($Distinguishedname.SubString($SourceNameIndex)) -replace 'DC=','' -replace ',','.' } else { $SourceDomain = "" } $TargetNameIndex = $Distinguishedname.IndexOf(',CN=System') if ($SourceNameIndex) { $TargetDomain = $Distinguishedname.SubString(3, $TargetNameIndex-3) } else { $TargetDomain = "" } $ObjectGuid = New-Object Guid @(,$Props.objectguid[0]) $TargetSID = (New-Object System.Security.Principal.SecurityIdentifier($Props.securityidentifier[0],0)).Value $DomainTrust | Add-Member Noteproperty 'SourceName' $SourceDomain $DomainTrust | Add-Member Noteproperty 'TargetName' $Props.name[0] # $DomainTrust | Add-Member Noteproperty 'TargetGuid' "{$ObjectGuid}" $DomainTrust | Add-Member Noteproperty 'TrustType' $TrustType $DomainTrust | Add-Member Noteproperty 'TrustAttributes' $($TrustAttrib -join ',') $DomainTrust | Add-Member Noteproperty 'TrustDirection' "$Direction" $DomainTrust | Add-Member Noteproperty 'WhenCreated' $Props.whencreated[0] $DomainTrust | Add-Member Noteproperty 'WhenChanged' $Props.whenchanged[0] $DomainTrust.PSObject.TypeNames.Insert(0, 'PowerView.DomainTrust.LDAP') $DomainTrust } if ($Results) { try { $Results.dispose() } catch { Write-Verbose "[Get-DomainTrust] Error disposing of the Results object: $_" } } $TrustSearcher.dispose() } } elseif ($PsCmdlet.ParameterSetName -eq 'API') { # if we're searching for domain trusts through Win32 API functions if ($PSBoundParameters['Server']) { $TargetDC = $Server } elseif ($Domain -and $Domain.Trim() -ne '') { $TargetDC = $Domain } else { # see https://msdn.microsoft.com/en-us/library/ms675976(v=vs.85).aspx for default NULL behavior $TargetDC = $Null } # arguments for DsEnumerateDomainTrusts $PtrInfo = [IntPtr]::Zero # 63 = DS_DOMAIN_IN_FOREST + DS_DOMAIN_DIRECT_OUTBOUND + DS_DOMAIN_TREE_ROOT + DS_DOMAIN_PRIMARY + DS_DOMAIN_NATIVE_MODE + DS_DOMAIN_DIRECT_INBOUND $Flags = 63 $DomainCount = 0 # get the trust information from the target server $Result = $Netapi32::DsEnumerateDomainTrusts($TargetDC, $Flags, [ref]$PtrInfo, [ref]$DomainCount) # Locate the offset of the initial intPtr $Offset = $PtrInfo.ToInt64() # 0 = success if (($Result -eq 0) -and ($Offset -gt 0)) { # Work out how much to increment the pointer by finding out the size of the structure $Increment = $DS_DOMAIN_TRUSTS::GetSize() # parse all the result structures for ($i = 0; ($i -lt $DomainCount); $i++) { # create a new int ptr at the given offset and cast the pointer as our result structure $NewIntPtr = New-Object System.Intptr -ArgumentList $Offset $Info = $NewIntPtr -as $DS_DOMAIN_TRUSTS $Offset = $NewIntPtr.ToInt64() $Offset += $Increment $SidString = '' $Result = $Advapi32::ConvertSidToStringSid($Info.DomainSid, [ref]$SidString);$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error() if ($Result -eq 0) { Write-Verbose "[Get-DomainTrust] Error: $(([ComponentModel.Win32Exception] $LastError).Message)" } else { $DomainTrust = New-Object PSObject $DomainTrust | Add-Member Noteproperty 'SourceName' $SourceDomain $DomainTrust | Add-Member Noteproperty 'TargetName' $Info.DnsDomainName $DomainTrust | Add-Member Noteproperty 'TargetNetbiosName' $Info.NetbiosDomainName $DomainTrust | Add-Member Noteproperty 'Flags' $Info.Flags $DomainTrust | Add-Member Noteproperty 'ParentIndex' $Info.ParentIndex $DomainTrust | Add-Member Noteproperty 'TrustType' $Info.TrustType $DomainTrust | Add-Member Noteproperty 'TrustAttributes' $Info.TrustAttributes $DomainTrust | Add-Member Noteproperty 'TargetSid' $SidString $DomainTrust | Add-Member Noteproperty 'TargetGuid' $Info.DomainGuid $DomainTrust.PSObject.TypeNames.Insert(0, 'PowerView.DomainTrust.API') $DomainTrust } } # free up the result buffer $Null = $Netapi32::NetApiBufferFree($PtrInfo) } else { Write-Verbose "[Get-DomainTrust] Error: $(([ComponentModel.Win32Exception] $Result).Message)" } } else { # if we're searching for domain trusts through .NET methods $FoundDomain = Get-Domain @NetSearcherArguments if ($FoundDomain) { $FoundDomain.GetAllTrustRelationships() | ForEach-Object { $_.PSObject.TypeNames.Insert(0, 'PowerView.DomainTrust.NET') $_ } } } } } function Get-ForestTrust { <# .SYNOPSIS Return all forest trusts for the current forest or a specified forest. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-Forest .DESCRIPTION This function will enumerate domain trust relationships for the current (or a remote) forest using number of method using the .NET method GetAllTrustRelationships() on a System.DirectoryServices.ActiveDirectory.Forest returned by Get-Forest. .PARAMETER Forest Specifies the forest to query for trusts, defaults to the current forest. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to the target domain. .EXAMPLE Get-ForestTrust Return current forest trusts. .EXAMPLE Get-ForestTrust -Forest "external.local" Return trusts for the "external.local" forest. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-ForestTrust -Forest "external.local" -Credential $Cred Return trusts for the "external.local" forest using the specified alternate credenitals. .OUTPUTS PowerView.DomainTrust.NET A TrustRelationshipInformationCollection returned when using .NET methods (default). #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.ForestTrust.NET')] [CmdletBinding()] Param( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('Name')] [ValidateNotNullOrEmpty()] [String] $Forest, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) PROCESS { $NetForestArguments = @{} if ($PSBoundParameters['Forest']) { $NetForestArguments['Forest'] = $Forest } if ($PSBoundParameters['Credential']) { $NetForestArguments['Credential'] = $Credential } $FoundForest = Get-Forest @NetForestArguments if ($FoundForest) { $FoundForest.GetAllTrustRelationships() | ForEach-Object { $_.PSObject.TypeNames.Insert(0, 'PowerView.ForestTrust.NET') $_ } } } } function Get-DomainForeignUser { <# .SYNOPSIS Enumerates users who are in groups outside of the user's domain. This is a domain's "outgoing" access. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-Domain, Get-DomainUser .DESCRIPTION Uses Get-DomainUser to enumerate all users for the current (or target) domain, then calculates the given user's domain name based on the user's distinguishedName. This domain name is compared to the queried domain, and the user object is output if they differ. .PARAMETER Domain Specifies the domain to use for the query, defaults to the current domain. .PARAMETER LDAPFilter Specifies an LDAP query string that is used to filter Active Directory objects. .PARAMETER Properties Specifies the properties of the output object to retrieve from the server. .PARAMETER SearchBase The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local" Useful for OU queries. .PARAMETER Server Specifies an Active Directory server (domain controller) to bind to. .PARAMETER SearchScope Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree). .PARAMETER ResultPageSize Specifies the PageSize to set for the LDAP searcher object. .PARAMETER ServerTimeLimit Specifies the maximum amount of time the server spends searching. Default of 120 seconds. .PARAMETER SecurityMasks Specifies an option for examining security information of a directory object. One of 'Dacl', 'Group', 'None', 'Owner', 'Sacl'. .PARAMETER Tombstone Switch. Specifies that the searcher should also return deleted/tombstoned objects. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to the target domain. .EXAMPLE Get-DomainForeignUser Return all users in the current domain who are in groups not in the current domain. .EXAMPLE Get-DomainForeignUser -Domain dev.testlab.local Return all users in the dev.testlab.local domain who are in groups not in the dev.testlab.local domain. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainForeignUser -Domain dev.testlab.local -Server secondary.dev.testlab.local -Credential $Cred Return all users in the dev.testlab.local domain who are in groups not in the dev.testlab.local domain, binding to the secondary.dev.testlab.local for queries, and using the specified alternate credentials. .OUTPUTS PowerView.ForeignUser Custom PSObject with translated user property fields. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.ForeignUser')] [CmdletBinding()] Param( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('Name')] [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, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $SearcherArguments = @{} $SearcherArguments['LDAPFilter'] = '(memberof=*)' 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 } if ($PSBoundParameters['Raw']) { $SearcherArguments['Raw'] = $Raw } } PROCESS { Get-DomainUser @SearcherArguments | ForEach-Object { ForEach ($Membership in $_.memberof) { $Index = $Membership.IndexOf('DC=') if ($Index) { $GroupDomain = $($Membership.SubString($Index)) -replace 'DC=','' -replace ',','.' $UserDistinguishedName = $_.distinguishedname $UserIndex = $UserDistinguishedName.IndexOf('DC=') $UserDomain = $($_.distinguishedname.SubString($UserIndex)) -replace 'DC=','' -replace ',','.' if ($GroupDomain -ne $UserDomain) { # if the group domain doesn't match the user domain, display it $GroupName = $Membership.Split(',')[0].split('=')[1] $ForeignUser = New-Object PSObject $ForeignUser | Add-Member Noteproperty 'UserDomain' $UserDomain $ForeignUser | Add-Member Noteproperty 'UserName' $_.samaccountname $ForeignUser | Add-Member Noteproperty 'UserDistinguishedName' $_.distinguishedname $ForeignUser | Add-Member Noteproperty 'GroupDomain' $GroupDomain $ForeignUser | Add-Member Noteproperty 'GroupName' $GroupName $ForeignUser | Add-Member Noteproperty 'GroupDistinguishedName' $Membership $ForeignUser.PSObject.TypeNames.Insert(0, 'PowerView.ForeignUser') $ForeignUser } } } } } } function Get-DomainForeignGroupMember { <# .SYNOPSIS Enumerates groups with users outside of the group's domain and returns each foreign member. This is a domain's "incoming" access. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-Domain, Get-DomainGroup .DESCRIPTION Uses Get-DomainGroup to enumerate all groups for the current (or target) domain, then enumerates the members of each group, and compares the member's domain name to the parent group's domain name, outputting the member if the domains differ. .PARAMETER Domain Specifies the domain to use for the query, defaults to the current domain. .PARAMETER LDAPFilter Specifies an LDAP query string that is used to filter Active Directory objects. .PARAMETER Properties Specifies the properties of the output object to retrieve from the server. .PARAMETER SearchBase The LDAP source to search through, e.g. "LDAP://OU=secret,DC=testlab,DC=local" Useful for OU queries. .PARAMETER Server Specifies an Active Directory server (domain controller) to bind to. .PARAMETER SearchScope Specifies the scope to search under, Base/OneLevel/Subtree (default of Subtree). .PARAMETER ResultPageSize Specifies the PageSize to set for the LDAP searcher object. .PARAMETER ServerTimeLimit Specifies the maximum amount of time the server spends searching. Default of 120 seconds. .PARAMETER SecurityMasks Specifies an option for examining security information of a directory object. One of 'Dacl', 'Group', 'None', 'Owner', 'Sacl'. .PARAMETER Tombstone Switch. Specifies that the searcher should also return deleted/tombstoned objects. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to the target domain. .EXAMPLE Get-DomainForeignGroupMember Return all group members in the current domain where the group and member differ. .EXAMPLE Get-DomainForeignGroupMember -Domain dev.testlab.local Return all group members in the dev.testlab.local domain where the member is not in dev.testlab.local. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainForeignGroupMember -Domain dev.testlab.local -Server secondary.dev.testlab.local -Credential $Cred Return all group members in the dev.testlab.local domain where the member is not in dev.testlab.local. binding to the secondary.dev.testlab.local for queries, and using the specified alternate credentials. .OUTPUTS PowerView.ForeignGroupMember Custa643b580-b5af-4819-96e0-1b706cd7fc22C:\Users\Administrator\AppData\Local\Temp\PowerView.ps1