Skip to content

Commit

Permalink
Add Misc > Invoke-UserSessionListCheck
Browse files Browse the repository at this point in the history
  • Loading branch information
itm4n committed Feb 18, 2022
1 parent 3f2d58f commit 885c774
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 6 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
2022-02-18
- Added Misc > Invoke-UserSessionListCheck

2022-02-13
- Added Config > Invoke-HardenedUNCPathCheck (@mr_mitm, @itm4n)

Expand Down
8 changes: 4 additions & 4 deletions PrivescCheck.ps1

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/00_Main.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ function Invoke-PrivescCheck {
"MISC_DEFENDER_EXCLUSIONS", "Invoke-DefenderExclusionsCheck", "Misc", "Defender exclusions", "Info", "Info", "Table", "True", "True", "False", "List Microsoft Defender exclusions that were configured both locally and through GPO."
"MISC_SYSINFO", "Invoke-SystemInfoCheck", "Misc", "OS Version", "Info", "Info", "Table", "True", "True", "False", "Print the detailed version number of the Operating System. If we can't get the update history, this might be useful."
"MISC_ADMINS", "Invoke-LocalAdminGroupCheck", "Misc", "Local Admin Group", "Info", "Info", "Table", "True", "True", "False", "Enumerate the users and groups that belong to the local 'Administrators' group."
"MISC_USER_SESSION_LIST", "Invoke-UserSessionListCheck", "Misc", "User session list", "Info", "Info", "Table", "False", "True", "False", "Enumerate the sessions of the currently logged-on users. It might be possible to capture or relay the NTLM/Kerberos authentication of these users (RemotePotato0, KrbRelay)."
"MISC_HOMES", "Invoke-UsersHomeFolderCheck", "Misc", "User Home Folders", "Info", "Info", "Table", "True", "False", "False", "Enumerate local HOME folders and check for potentially weak permissions."
"MISC_MACHINE_ROLE", "Invoke-MachineRoleCheck", "Misc", "Machine Role", "Info", "Info", "Table", "True", "True", "False", "Simply return the machine's role. It can be either 'Workstation', 'Server' or 'Domain Controller'."
"MISC_STARTUP_EVENTS", "Invoke-SystemStartupHistoryCheck", "Misc", "System Startup History", "Info", "Info", "Table", "True", "True", "False", "Retrieve the machine's startup history. This might be useful to figure out how often a server is rebooted. In the case of a workstation, such metric isn't as relevant."
Expand Down
32 changes: 30 additions & 2 deletions src/01_Win32.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,19 @@ $IP_ADAPTER_FLAGS = New-Enum $Module WinApiModule.IP_ADAPTER_FLAGS UInt32 @{
Ipv6ManagedAddressConfigurationSupported = 0x00000200
} -Bitfield

$WTS_CONNECTSTATE_CLASS = New-Enum $Module WinApiModule.WTS_CONNECTSTATE_CLASS UInt32 @{
Active = '0x00000000'
Connected = '0x00000001'
ConnectQuery = '0x00000002'
Shadow = '0x00000003'
Disconnected = '0x00000004'
Idle = '0x00000005'
Listen = '0x00000006'
Reset = '0x00000007'
Down = '0x00000008'
Init = '0x00000009'
}

$LARGE_INTEGER = New-Structure $Module WinApiModule.LARGE_INTEGER @{
LowPart = New-StructureField 0 UInt32
HighPart = New-StructureField 1 Int32
Expand Down Expand Up @@ -1217,6 +1230,17 @@ $WIN32_FILE_ATTRIBUTE_DATA = New-Structure $Module WinApiModule.WIN32_FILE_ATTRI
nFileSizeLow = New-StructureField 5 UInt32
}

$WTS_SESSION_INFO_1W = New-Structure $Module WinApiModule.WTS_SESSION_INFO_1W @{
ExecEnvId = New-StructureField 0 UInt32
State = New-StructureField 1 $WTS_CONNECTSTATE_CLASS
SessionId = New-StructureField 2 UInt32
SessionName = New-StructureField 3 String -MarshalAs @('LPWStr')
HostName = New-StructureField 4 String -MarshalAs @('LPWStr')
UserName = New-StructureField 5 String -MarshalAs @('LPWStr')
DomainName = New-StructureField 6 String -MarshalAs @('LPWStr')
FarmName = New-StructureField 7 String -MarshalAs @('LPWStr')
}

$FunctionDefinitions = @(
(New-Function advapi32 OpenSCManager ([IntPtr]) @([String], [String], [UInt32]) ([Runtime.InteropServices.CallingConvention]::Winapi) ([Runtime.InteropServices.CharSet]::Unicode) -SetLastError),
(New-Function advapi32 QueryServiceObjectSecurity ([Bool]) @([IntPtr], [Security.AccessControl.SecurityInfos], [Byte[]], [UInt32], [UInt32].MakeByRefType()) -SetLastError),
Expand Down Expand Up @@ -1265,7 +1289,10 @@ $FunctionDefinitions = @(
(New-Function wlanapi WlanEnumInterfaces ([UInt32]) @([IntPtr], [IntPtr], [IntPtr].MakeByRefType()) -EntryPoint WlanEnumInterfaces),
(New-Function wlanapi WlanFreeMemory ([Void]) @([IntPtr]) -EntryPoint WlanFreeMemory),
(New-Function wlanapi WlanGetProfileList ([UInt32]) @([IntPtr], [Guid], [IntPtr], [IntPtr].MakeByRefType()) -EntryPoint WlanGetProfileList),
(New-Function wlanapi WlanGetProfile ([UInt32]) @([IntPtr], [Guid], [String], [IntPtr], [String].MakeByRefType(), [UInt32].MakeByRefType(), [UInt32].MakeByRefType()) -EntryPoint WlanGetProfile)
(New-Function wlanapi WlanGetProfile ([UInt32]) @([IntPtr], [Guid], [String], [IntPtr], [String].MakeByRefType(), [UInt32].MakeByRefType(), [UInt32].MakeByRefType()) -EntryPoint WlanGetProfile),

(New-Function wtsapi32 WTSEnumerateSessionsEx ([Bool]) @([IntPtr], [UInt32].MakeByRefType(), [UInt32], [IntPtr].MakeByRefType(), [UInt32].MakeByRefType()) -SetLastError -EntryPoint WTSEnumerateSessionsExW),
(New-Function wtsapi32 WTSFreeMemoryEx ([Bool]) @([UInt32], [IntPtr], [UInt32]) -SetLastError -EntryPoint WTSFreeMemoryExW)
)

$Types = $FunctionDefinitions | Add-Win32Type -Module $Module -Namespace 'WinApiModule.NativeMethods'
Expand All @@ -1274,4 +1301,5 @@ $Iphlpapi = $Types['iphlpapi']
$Kernel32 = $Types['kernel32']
$Ntdll = $Types['ntdll']
$Vaultcli = $Types['vaultcli']
$Wlanapi = $Types['wlanapi']
$Wlanapi = $Types['wlanapi']
$Wtsapi32 = $Types['wtsapi32']
67 changes: 67 additions & 0 deletions src/02_Helpers.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3095,4 +3095,71 @@ function Test-IsDomainJoined {
$Item = Get-ItemProperty -Path "Registry::$($RegPath)" -Name $Value -ErrorAction SilentlyContinue

return (-not [string]::IsNullOrEmpty($Item.Value))
}

function Get-RemoteDesktopUserSessionList {
<#
.SYNOPSIS
List the sessions of the currently logged-on users through the WTS API.
Author: @itm4n
License: BSD 3-Clause
.DESCRIPTION
This cmdlet simply invokes the WTSEnumerateSessionsEx API to enumerate the sessions of the logged-on users. This API returns a list of TS_SESSION_INFO_1W structures containing the sessions info.
.EXAMPLE
PS C:\> Get-RemoteDesktopUserSessionList
ExecEnvId : 0
State : Disconnected
SessionId : 0
SessionName : Services
HostName :
UserName :
DomainName :
FarmName :
ExecEnvId : 1
State : Active
SessionId : 1
SessionName : Console
HostName :
UserName : lab-user
DomainName : DESKTOP-U7FQ7U5
FarmName :
#>

[CmdletBinding()] Param()

$Level = 1
$SessionInfoListPtr = [IntPtr] 0
$SessionInfoCount = [UInt32] 0

$Success = $Wtsapi32::WTSEnumerateSessionsEx(0, [ref]$Level, 0, [ref]$SessionInfoListPtr, [ref]$SessionInfoCount)
Write-Verbose "WTSEnumerateSessionsEx: $($Success) | Count: $($SessionInfoCount) | List: 0x$('{0:x16}' -f [Int64]$SessionInfoListPtr)"

if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "WTSEnumerateSessionsEx - $([ComponentModel.Win32Exception] $LastError)"
return
}

$SessionInfoPtr = $SessionInfoListPtr
for ($i = 0; $i -lt $SessionInfoCount; $i++) {

$SessionInfo = [Runtime.InteropServices.Marshal]::PtrToStructure($SessionInfoPtr, [type] $WTS_SESSION_INFO_1W)
$SessionInfo

$SessionInfoPtr = [IntPtr] ($SessionInfoPtr.ToInt64() + [Runtime.InteropServices.Marshal]::SizeOf([type] $WTS_SESSION_INFO_1W))
}

$Success = $Wtsapi32::WTSFreeMemoryEx(2, $SessionInfoListPtr, $SessionInfoCount)
Write-Verbose "WTSFreeMemoryEx: $($Success)"

if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "WTSFreeMemoryEx - $([ComponentModel.Win32Exception] $LastError)"
return
}
}
46 changes: 46 additions & 0 deletions src/99_Misc.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -794,4 +794,50 @@ function Invoke-DefenderExclusionsCheck {
}

$Exclusions | Sort-Object -Property "Type"
}

function Invoke-UserSessionListCheck {
<#
.SYNOPSIS
List the the sessions of the currently logged-on users (similar to the command 'query session').
Author: @itm4n
License: BSD 3-Clause
.DESCRIPTION
This check is essentially a wrapper for the helper function Get-RemoteDesktopUserSessionList.
.EXAMPLE
PS C:\> Invoke-UserSessionListCheck
SessionName UserName Id State
----------- -------- -- -----
Services 0 Disconnected
Console SRV01\Administrator 1 Active
RDP-Tcp#3 SANDBOX\Administrator 3 Active
#>

[CmdletBinding()] Param()

foreach ($Session in (Get-RemoteDesktopUserSessionList)) {

if ([String]::IsNullOrEmpty($Session.UserName)) {
$UserName = ""
}
else {
if ([String]::IsNullOrEmpty($Session.DomainName)) {
$UserName = $Session.UserName
}
else {
$UserName = "$($Session.DomainName)\$($Session.UserName)"
}
}

$Result = New-Object -TypeName PSObject
$Result | Add-Member -MemberType "NoteProperty" -Name "SessionName" -Value $Session.SessionName
$Result | Add-Member -MemberType "NoteProperty" -Name "UserName" -Value $UserName
$Result | Add-Member -MemberType "NoteProperty" -Name "Id" -Value $Session.SessionId
$Result | Add-Member -MemberType "NoteProperty" -Name "State" -Value $Session.State
$Result
}
}

0 comments on commit 885c774

Please sign in to comment.