Add a polling mode to Invoke-UserHunter

Repeatedly poll a set of target computers for user sessions. This could
be a useful technique for building a much better picture of current
sessions, but without having to communicate with every host.

The -Poll parameter is used to specify the duration for which polling
should occur. Each target computer is dedicated with a thread with
-Delay and -Jitter specifying how long to sleep between each session
enumeration attempt of an individual host.
This commit is contained in:
Jon Cave
2016-08-13 12:00:44 +01:00
parent 869badc7f1
commit fda456338f
+109 -77
View File
@@ -9480,6 +9480,11 @@ function Invoke-UserHunter {
The maximum concurrent threads to execute.
.PARAMETER Poll
Continuously poll for sessions for the given duration. Automatically
sets Threads to the number of computers being polled.
.EXAMPLE
PS C:\> Invoke-UserHunter -CheckAccess
@@ -9534,6 +9539,13 @@ function Invoke-UserHunter {
Executes old Invoke-StealthUserHunter functionality, enumerating commonly
used servers and checking just sessions for each.
.EXAMPLE
PS C:\> Invoke-UserHunter -Stealth -StealthSource DC -Poll 3600 -Delay 5 -ShowAll | ? { ! $_.UserName.EndsWith('$') }
Poll Domain Controllers in parallel for sessions for an hour, waiting five
seconds before querying each DC again and filtering out computer accounts.
.LINK
http://blog.harmj0y.net
#>
@@ -9623,7 +9635,10 @@ function Invoke-UserHunter {
[Int]
[ValidateRange(1,100)]
$Threads
$Threads,
[UInt32]
$Poll = 0
)
begin {
@@ -9632,9 +9647,6 @@ function Invoke-UserHunter {
$DebugPreference = 'Continue'
}
# random object for delay
$RandNo = New-Object System.Random
Write-Verbose "[*] Running Invoke-UserHunter with delay of $Delay"
#####################################################
@@ -9705,6 +9717,14 @@ function Invoke-UserHunter {
}
}
if ($Poll -gt 0) {
Write-Verbose "[*] Polling for $Poll seconds. Automatically enabling threaded mode."
if ($ComputerName.Count -gt 100) {
throw "Too many hosts to poll! Try fewer than 100."
}
$Threads = $ComputerName.Count
}
#####################################################
#
# Now we build the user target set
@@ -9802,7 +9822,7 @@ function Invoke-UserHunter {
# script block that enumerates a server
$HostEnumBlock = {
param($ComputerName, $Ping, $TargetUsers, $CurrentUser, $Stealth, $DomainShortName)
param($ComputerName, $Ping, $TargetUsers, $CurrentUser, $Stealth, $DomainShortName, $Poll, $Delay, $Jitter)
# optionally check if the server is up first
$Up = $True
@@ -9810,89 +9830,46 @@ function Invoke-UserHunter {
$Up = Test-Connection -Count 1 -Quiet -ComputerName $ComputerName
}
if($Up) {
if(!$DomainShortName) {
# if we're not searching for foreign users, check session information
$Sessions = Get-NetSession -ComputerName $ComputerName
ForEach ($Session in $Sessions) {
$UserName = $Session.sesi10_username
$CName = $Session.sesi10_cname
$Timer = [System.Diagnostics.Stopwatch]::StartNew()
$RandNo = New-Object System.Random
if($CName -and $CName.StartsWith("\\")) {
$CName = $CName.TrimStart("\")
}
Do {
if(!$DomainShortName) {
# if we're not searching for foreign users, check session information
$Sessions = Get-NetSession -ComputerName $ComputerName
ForEach ($Session in $Sessions) {
$UserName = $Session.sesi10_username
$CName = $Session.sesi10_cname
# make sure we have a result
if (($UserName) -and ($UserName.trim() -ne '') -and (!($UserName -match $CurrentUser))) {
$TargetUsers | Where-Object {$UserName -like $_.MemberName} | ForEach-Object {
$IPAddress = @(Get-IPAddress -ComputerName $ComputerName)[0].IPAddress
$FoundUser = New-Object PSObject
$FoundUser | Add-Member Noteproperty 'UserDomain' $_.MemberDomain
$FoundUser | Add-Member Noteproperty 'UserName' $UserName
$FoundUser | Add-Member Noteproperty 'ComputerName' $ComputerName
$FoundUser | Add-Member Noteproperty 'IPAddress' $IPAddress
$FoundUser | Add-Member Noteproperty 'SessionFrom' $CName
# Try to resolve the DNS hostname of $Cname
try {
$CNameDNSName = [System.Net.Dns]::GetHostEntry($CName) | Select-Object -ExpandProperty HostName
$FoundUser | Add-Member NoteProperty 'SessionFromName' $CnameDNSName
}
catch {
$FoundUser | Add-Member NoteProperty 'SessionFromName' $Null
}
# see if we're checking to see if we have local admin access on this machine
if ($CheckAccess) {
$Admin = Invoke-CheckLocalAdminAccess -ComputerName $CName
$FoundUser | Add-Member Noteproperty 'LocalAdmin' $Admin.IsAdmin
}
else {
$FoundUser | Add-Member Noteproperty 'LocalAdmin' $Null
}
$FoundUser.PSObject.TypeNames.Add('PowerView.UserSession')
$FoundUser
if($CName -and $CName.StartsWith("\\")) {
$CName = $CName.TrimStart("\")
}
}
}
}
if(!$Stealth) {
# if we're not 'stealthy', enumerate loggedon users as well
$LoggedOn = Get-NetLoggedon -ComputerName $ComputerName
ForEach ($User in $LoggedOn) {
$UserName = $User.wkui1_username
# TODO: translate domain to authoratative name
# then match domain name ?
$UserDomain = $User.wkui1_logon_domain
# make sure wet have a result
if (($UserName) -and ($UserName.trim() -ne '')) {
# make sure we have a result
if (($UserName) -and ($UserName.trim() -ne '') -and (!($UserName -match $CurrentUser))) {
$TargetUsers | Where-Object {$UserName -like $_.MemberName} | ForEach-Object {
$TargetUsers | Where-Object {$UserName -like $_.MemberName} | ForEach-Object {
$Proceed = $True
if($DomainShortName) {
if ($DomainShortName.ToLower() -ne $UserDomain.ToLower()) {
$Proceed = $True
}
else {
$Proceed = $False
}
}
if($Proceed) {
$IPAddress = @(Get-IPAddress -ComputerName $ComputerName)[0].IPAddress
$FoundUser = New-Object PSObject
$FoundUser | Add-Member Noteproperty 'UserDomain' $UserDomain
$FoundUser | Add-Member Noteproperty 'UserDomain' $_.MemberDomain
$FoundUser | Add-Member Noteproperty 'UserName' $UserName
$FoundUser | Add-Member Noteproperty 'ComputerName' $ComputerName
$FoundUser | Add-Member Noteproperty 'IPAddress' $IPAddress
$FoundUser | Add-Member Noteproperty 'SessionFrom' $Null
$FoundUser | Add-Member Noteproperty 'SessionFromName' $Null
$FoundUser | Add-Member Noteproperty 'SessionFrom' $CName
# Try to resolve the DNS hostname of $Cname
try {
$CNameDNSName = [System.Net.Dns]::GetHostEntry($CName) | Select-Object -ExpandProperty HostName
$FoundUser | Add-Member NoteProperty 'SessionFromName' $CnameDNSName
}
catch {
$FoundUser | Add-Member NoteProperty 'SessionFromName' $Null
}
# see if we're checking to see if we have local admin access on this machine
if ($CheckAccess) {
$Admin = Invoke-CheckLocalAdminAccess -ComputerName $ComputerName
$Admin = Invoke-CheckLocalAdminAccess -ComputerName $CName
$FoundUser | Add-Member Noteproperty 'LocalAdmin' $Admin.IsAdmin
}
else {
@@ -9904,10 +9881,61 @@ function Invoke-UserHunter {
}
}
}
}
if(!$Stealth) {
# if we're not 'stealthy', enumerate loggedon users as well
$LoggedOn = Get-NetLoggedon -ComputerName $ComputerName
ForEach ($User in $LoggedOn) {
$UserName = $User.wkui1_username
# TODO: translate domain to authoratative name
# then match domain name ?
$UserDomain = $User.wkui1_logon_domain
# make sure wet have a result
if (($UserName) -and ($UserName.trim() -ne '')) {
$TargetUsers | Where-Object {$UserName -like $_.MemberName} | ForEach-Object {
$Proceed = $True
if($DomainShortName) {
if ($DomainShortName.ToLower() -ne $UserDomain.ToLower()) {
$Proceed = $True
}
else {
$Proceed = $False
}
}
if($Proceed) {
$IPAddress = @(Get-IPAddress -ComputerName $ComputerName)[0].IPAddress
$FoundUser = New-Object PSObject
$FoundUser | Add-Member Noteproperty 'UserDomain' $UserDomain
$FoundUser | Add-Member Noteproperty 'UserName' $UserName
$FoundUser | Add-Member Noteproperty 'ComputerName' $ComputerName
$FoundUser | Add-Member Noteproperty 'IPAddress' $IPAddress
$FoundUser | Add-Member Noteproperty 'SessionFrom' $Null
$FoundUser | Add-Member Noteproperty 'SessionFromName' $Null
# see if we're checking to see if we have local admin access on this machine
if ($CheckAccess) {
$Admin = Invoke-CheckLocalAdminAccess -ComputerName $ComputerName
$FoundUser | Add-Member Noteproperty 'LocalAdmin' $Admin.IsAdmin
}
else {
$FoundUser | Add-Member Noteproperty 'LocalAdmin' $Null
}
$FoundUser.PSObject.TypeNames.Add('PowerView.UserSession')
$FoundUser
}
}
}
}
}
if ($Poll -gt 0) {
Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)
}
} While ($Poll -gt 0 -and $Timer.Elapsed.TotalSeconds -lt $Poll)
}
}
}
process {
@@ -9922,6 +9950,9 @@ function Invoke-UserHunter {
'CurrentUser' = $CurrentUser
'Stealth' = $Stealth
'DomainShortName' = $DomainShortName
'Poll' = $Poll
'Delay' = $Delay
'Jitter' = $Jitter
}
# kick off the threaded script block + arguments
@@ -9937,6 +9968,7 @@ function Invoke-UserHunter {
Write-Verbose "[*] Total number of active hosts: $($ComputerName.count)"
$Counter = 0
$RandNo = New-Object System.Random
ForEach ($Computer in $ComputerName) {
@@ -9946,7 +9978,7 @@ function Invoke-UserHunter {
Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay)
Write-Verbose "[*] Enumerating server $Computer ($Counter of $($ComputerName.count))"
$Result = Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $Computer, $False, $TargetUsers, $CurrentUser, $Stealth, $DomainShortName
$Result = Invoke-Command -ScriptBlock $HostEnumBlock -ArgumentList $Computer, $False, $TargetUsers, $CurrentUser, $Stealth, $DomainShortName, 0, 0, 0
$Result
if($Result -and $StopOnSuccess) {