Skip to content

Instantly share code, notes, and snippets.

@jborean93
Created August 30, 2022 11:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jborean93/388784b02b6b2a69e630e3bc0b8b2616 to your computer and use it in GitHub Desktop.
Save jborean93/388784b02b6b2a69e630e3bc0b8b2616 to your computer and use it in GitHub Desktop.
Get LSA logon session data
# Copyright: (c) 2022, Jordan Borean (@jborean93) <jborean93@gmail.com>
# MIT License (see LICENSE or https://opensource.org/licenses/MIT)
Function Get-LogonSessionData {
<#
.SYNOPSIS
Get LSA logon session data.
.DESCRIPTION
Get the logon session information for all or a specific logon session or specific process logon sessions.
.PARAMETER LogonId
A list of logon sessions to retrieve the data for.
Will retrieve all logon sessions if not set or an empty array is specified.
.PARAMETER ProcessId
A list of process ids to retrieve the data for.
.PARAMETER Current
Retrieve the logon session data that the current process sits in.
.EXAMPLE Get logon session data for all sessions
Get-LogonSessionData
.EXAMPLE Get logon session data for the current process
Get-LogonSessionData -Current
.NOTES
To retrieve logon session data for other sessions you need to be running with administrative rights.
#>
[OutputType("LSA.LogonSessionData")]
[CmdletBinding(DefaultParameterSetName = "LogonId")]
param(
[Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName, ParameterSetName = "LogonId")]
[AllowEmptyCollection()]
[int64[]]
$LogonId,
[Parameter(ValueFromPipelineByPropertyName, ParameterSetName = "ProcessId")]
[int[]]
$ProcessId,
[Parameter(ParameterSetName = "Current")]
[switch]
$Current
)
begin {
Add-Type -TypeDefinition @'
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;
namespace LSA
{
public struct NativeHelpers
{
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public UInt32 LowPart;
public Int32 HighPart;
public static explicit operator Int64(LUID luid)
{
return ((Int64)luid.HighPart) << 32 | luid.LowPart;
}
public static explicit operator LUID(Int64 value)
{
return new LUID()
{
LowPart = (UInt32)(value & 0xFFFFFFFF),
HighPart = (Int32)(value >> 32),
};
}
}
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_STATISTICS
{
public LUID TokenId;
public LUID AuthenticationId;
public UInt64 ExpirationTime;
public UInt32 TokenType;
public UInt32 ImpersonationLevel;
public UInt32 DynamicCharged;
public UInt32 DynamicAvailable;
public UInt32 GroupCount;
public UInt32 PrivilegeCount;
public LUID ModifiedId;
}
}
[Flags]
public enum ProcessAccessRights
{
Terminate = 0x00000001,
CreateThread = 0x00000002,
VMOperation = 0x00000008,
VMRead = 0x00000010,
VMWrite = 0x00000020,
DupHandle = 0x00000040,
CreateProcess = 0x00000080,
SetQuota = 0x00000100,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
SuspendResume = 0x00000800,
QueryLimitedInformation = 0x00001000,
Delete = 0x00010000,
ReadControl = 0x00020000,
WriteDAC = 0x00040000,
WriteOwner = 0x00080000,
Synchronize = 0x00100000,
AccessSystemSecurity = 0x01000000,
StandardRightsAll = Delete | ReadControl | WriteDAC | WriteOwner | Synchronize,
StandardRightsExecute = ReadControl,
StandardRightsRead = ReadControl,
StandardRightsRequired = Delete | ReadControl | WriteDAC | WriteOwner,
StandardRightsWrite = ReadControl,
GenericAll = 0x10000000,
GenericExecute = 0x20000000,
GenericWrite = 0x40000000,
GenericRead = -2147483648,
AllAccess = StandardRightsRequired | Synchronize | 0x1FFF,
}
public enum SecurityLogonType : uint
{
Undefined = 0,
Interactive = 2,
Network,
Batch,
Service,
Proxy,
Unlock,
NetworkCleartext,
NewCredentials,
RemoteInteractive,
CachedInteractive,
CachedRemoteInteractive,
CachedUnlock,
}
[Flags]
public enum UserFlags : uint
{
None = 0x00000000,
Guest = 0x00000001,
NoEncryption = 0x00000002,
CachedAccount = 0x00000004,
UsedLmPassword = 0x00000008,
ExtraSids = 0x00000020,
SubAuthSessionKey = 0x00000040,
ServerTrustAccount = 0x00000080,
NtlmV2Enabled = 0x00000100,
ResourceGroups = 0x00000200,
ProfilePathReturned = 0x00000400,
NtV2 = 0x00000800,
LmV2 = 0x00001000,
NtlmV2 = 0x00002000,
Optimized = 0x00004000,
WinLogon = 0x00008000,
Pkinit = 0x00010000,
NotOptimized = 0x00020000,
NoElevation = 0x00040000,
ManagedService = 0x00080000,
}
public class LogonSessionData
{
public Int64 LogonId { get; internal set; }
public string UserName { get; internal set; }
public string LogonDomain { get; internal set; }
public string AuthenticationPackage { get; internal set; }
public SecurityLogonType LogonType { get; internal set; }
public int SessionId { get; internal set; }
public SecurityIdentifier Identity { get; internal set; }
public DateTime? LogonTime { get; internal set; }
public string LogonServer { get; internal set; }
public string DnsDomainName { get; internal set; }
public string Upn { get; internal set; }
public UserFlags UserFlags { get; internal set; }
public DateTime? LastSuccessfulLogon { get; internal set; }
public DateTime? LastFailedLogon { get; internal set; }
public int FailedAttemptCountSinceLastSuccessfulLogon { get; internal set; }
public string LogonScript { get; internal set; }
public string ProfilePath { get; internal set; }
public string HomeDirectory { get; internal set; }
public string HomeDirectoryDrive { get; internal set; }
public DateTime? LogoffTime { get; internal set; }
public DateTime? KickOffTime { get; internal set; }
public DateTime? PasswordLastSet { get; internal set; }
public DateTime? PasswordCanChange { get; internal set; }
public DateTime? PasswordMustChange { get; internal set; }
}
public static class LogonSession
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct LSA_UNICODE_STRING
{
public UInt16 Length;
public UInt16 MaximumLength;
public IntPtr buffer;
public static explicit operator string(LSA_UNICODE_STRING value)
{
if (value.buffer == IntPtr.Zero)
{
return null;
}
return Marshal.PtrToStringUni(value.buffer, (int)value.Length / 2);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_LOGON_SESSION_DATA
{
public UInt32 Size;
public NativeHelpers.LUID LogonId;
public LSA_UNICODE_STRING Username;
public LSA_UNICODE_STRING LogonDomain;
public LSA_UNICODE_STRING AuthenticationPackage;
public SecurityLogonType LogonType;
public Int32 Session;
public IntPtr Sid;
public Int64 LogonTime;
public LSA_UNICODE_STRING LogonServer;
public LSA_UNICODE_STRING DnsDomainName;
public LSA_UNICODE_STRING Upn;
public UserFlags UserFlags;
public LSA_LAST_INTER_LOGON_INFO LastLogonInfo;
public LSA_UNICODE_STRING LogonScript;
public LSA_UNICODE_STRING ProfilePath;
public LSA_UNICODE_STRING HomeDirectory;
public LSA_UNICODE_STRING HomeDirectoryDrive;
public Int64 LogoffTime;
public Int64 KickOffTime;
public Int64 PasswordLastSet;
public Int64 PasswordCanChange;
public Int64 PasswordMustChange;
}
[StructLayout(LayoutKind.Sequential)]
public struct LSA_LAST_INTER_LOGON_INFO
{
public Int64 LastSuccessfulLogon;
public Int64 LastFailedLogon;
public Int32 FailedAttemptCountSinceLastSuccessfulLogon;
}
[DllImport("Secur32.dll", EntryPoint = "LsaEnumerateLogonSessions")]
private static extern UInt32 NativeLsaEnumerateLogonSessions(
out Int32 LogonSessionCount,
out IntPtr LogonSessionList);
public static NativeHelpers.LUID[] LsaEnumerateLogonSessions()
{
int sessionCount;
IntPtr buffer;
UInt32 res = NativeLsaEnumerateLogonSessions(out sessionCount, out buffer);
if (res != 0)
{
throw new Win32Exception(LsaNtStatusToWinError(res));
}
try
{
IntPtr currentId = buffer;
List<NativeHelpers.LUID> sessions = new List<NativeHelpers.LUID>();
for (int i = 0; i < sessionCount; i++)
{
NativeHelpers.LUID logonSession = Marshal.PtrToStructure<NativeHelpers.LUID>(currentId);
sessions.Add(logonSession);
currentId = IntPtr.Add(currentId, Marshal.SizeOf<NativeHelpers.LUID>());
}
return sessions.ToArray();
}
finally
{
LsaFreeReturnBuffer(buffer);
}
}
[DllImport("Secur32.dll", EntryPoint = "LsaGetLogonSessionData")]
private static extern UInt32 NativeLsaGetLogonSessionData(
ref NativeHelpers.LUID LogonId,
out IntPtr ppLogonSessionData);
public static LogonSessionData LsaGetLogonSessionData(NativeHelpers.LUID logonId)
{
IntPtr buffer;
UInt32 res = NativeLsaGetLogonSessionData(ref logonId, out buffer);
if (res != 0)
{
throw new Win32Exception(LsaNtStatusToWinError(res));
}
try
{
SECURITY_LOGON_SESSION_DATA data = Marshal.PtrToStructure<SECURITY_LOGON_SESSION_DATA>(buffer);
return new LogonSessionData()
{
LogonId = (Int64)data.LogonId,
UserName = (string)data.Username,
LogonDomain = (string)data.LogonDomain,
AuthenticationPackage = (string)data.AuthenticationPackage,
LogonType = data.LogonType,
SessionId = data.Session,
Identity = data.Sid == IntPtr.Zero ? null : new SecurityIdentifier(data.Sid),
LogonTime = data.LogonTime == Int64.MaxValue
? (DateTime?)null : DateTime.FromFileTimeUtc(data.LogonTime),
LogonServer = (string)data.LogonServer,
DnsDomainName = (string)data.DnsDomainName,
Upn = (string)data.Upn,
UserFlags = data.UserFlags,
LastSuccessfulLogon = data.LastLogonInfo.LastSuccessfulLogon == 0
? (DateTime?)null : DateTime.FromFileTimeUtc(data.LastLogonInfo.LastSuccessfulLogon),
LastFailedLogon = data.LastLogonInfo.LastFailedLogon == 0
? (DateTime?)null : DateTime.FromFileTimeUtc(data.LastLogonInfo.LastFailedLogon),
FailedAttemptCountSinceLastSuccessfulLogon = data.LastLogonInfo.FailedAttemptCountSinceLastSuccessfulLogon,
LogonScript = (string)data.LogonScript,
ProfilePath = (string)data.ProfilePath,
HomeDirectory = (string)data.HomeDirectory,
HomeDirectoryDrive = (string)data.HomeDirectoryDrive,
LogoffTime = data.LogoffTime == Int64.MaxValue
? (DateTime?)null : DateTime.FromFileTimeUtc(data.LogoffTime),
KickOffTime = data.KickOffTime == Int64.MaxValue
? (DateTime?)null : DateTime.FromFileTimeUtc(data.KickOffTime),
PasswordLastSet = data.PasswordLastSet == Int64.MaxValue
? (DateTime?)null : DateTime.FromFileTimeUtc(data.PasswordLastSet),
PasswordCanChange = data.PasswordCanChange == Int64.MaxValue
? (DateTime?)null : DateTime.FromFileTimeUtc(data.PasswordCanChange),
PasswordMustChange = data.PasswordMustChange == Int64.MaxValue
? (DateTime?)null : DateTime.FromFileTimeUtc(data.PasswordMustChange),
};
}
finally
{
LsaFreeReturnBuffer(buffer);
}
}
[DllImport("Secur32.dll")]
private static extern UInt32 LsaFreeReturnBuffer(
IntPtr Buffer);
[DllImport("Secur32.dll")]
private static extern Int32 LsaNtStatusToWinError(
UInt32 Status);
}
public static class AccessToken
{
private enum TOKEN_INFORMATION_CLASS
{
TokenUser = 1,
TokenGroups,
TokenPrivileges,
TokenOwner,
TokenPrimaryGroup,
TokenDefaultDacl,
TokenSource,
TokenType,
TokenImpersonationLevel,
TokenStatistics,
TokenRestrictedSids,
TokenSessionId,
TokenGroupsAndPrivileges,
TokenSessionReference,
TokenSandBoxInert,
TokenAuditPolicy,
TokenOrigin,
TokenElevationType,
TokenLinkedToken,
TokenElevation,
TokenHasRestrictions,
TokenAccessInformation,
TokenVirtualizationAllowed,
TokenVirtualizationEnabled,
TokenIntegrityLevel,
TokenUIAccess,
TokenMandatoryPolicy,
TokenLogonSid,
TokenIsAppContainer,
TokenCapabilities,
TokenAppContainerSid,
TokenAppContainerNumber,
TokenUserClaimAttributes,
TokenDeviceClaimAttributes,
TokenRestrictedUserClaimAttributes,
TokenRestrictedDeviceClaimAttributes,
TokenDeviceGroups,
TokenRestrictedDeviceGroups,
TokenSecurityAttributes,
TokenIsRestricted,
MaxTokenInfoClass
}
[DllImport("Kernel32.dll", EntryPoint = "GetCurrentProcess")]
private static extern IntPtr NativeGetCurrentProcess();
public static SafeProcessHandle GetCurrentProcess()
{
return new SafeProcessHandle(NativeGetCurrentProcess(), false);
}
[DllImport("Advapi32.dll", EntryPoint = "OpenProcessToken", SetLastError = true)]
private static extern bool NativeOpenProcessToken(
SafeProcessHandle ProcessHandle,
TokenAccessLevels DesiredAccess,
out SafeAccessTokenHandle TokenHandle);
public static SafeAccessTokenHandle OpenProcessToken(SafeProcessHandle process, TokenAccessLevels access)
{
SafeAccessTokenHandle token;
if (!NativeOpenProcessToken(process, access, out token))
{
throw new Win32Exception();
}
return token;
}
[DllImport("Advapi32.dll", SetLastError = true)]
private static extern bool GetTokenInformation(
SafeAccessTokenHandle TokenHandle,
TOKEN_INFORMATION_CLASS TokenInformationClass,
IntPtr TokenInformation,
int TokenInformationLength,
out UInt32 ReturnLength);
public static NativeHelpers.TOKEN_STATISTICS GetTokenStatistics(SafeAccessTokenHandle token)
{
int bufferLength = Marshal.SizeOf<NativeHelpers.TOKEN_STATISTICS>();
IntPtr buffer = Marshal.AllocHGlobal(bufferLength);
try
{
uint returnLength;
if (!GetTokenInformation(token, TOKEN_INFORMATION_CLASS.TokenStatistics, buffer,
bufferLength, out returnLength))
{
throw new Win32Exception();
}
return Marshal.PtrToStructure<NativeHelpers.TOKEN_STATISTICS>(buffer);
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
[DllImport("Kernel32.dll", EntryPoint = "OpenProcess", SetLastError = true)]
private static extern SafeProcessHandle NativeOpenProcess(
ProcessAccessRights dwDesiredAccess,
bool bInheritHandle,
int dwProcessId);
public static SafeProcessHandle OpenProcess(int processId, ProcessAccessRights access, bool inherit)
{
SafeProcessHandle handle = NativeOpenProcess(access, inherit, processId);
if (handle.IsInvalid)
{
throw new Win32Exception();
}
return handle;
}
}
}
'@
}
process {
if ($PSCmdlet.ParameterSetName -eq "Current") {
try {
$currentToken = [LSA.AccessToken]::OpenProcessToken(
[LSA.AccessToken]::GetCurrentProcess(),
[System.Security.Principal.TokenAccessLevels]::Query)
$tokenStat = [LSA.AccessToken]::GetTokenStatistics($currentToken)
[LSA.LogonSession]::LsaGetLogonSessionData($tokenStat.AuthenticationId)
}
catch {
$err = [System.Management.Automation.ErrorRecord]::new(
$_.Exception,
"OpenCurrentProcess",
[System.Management.Automation.ErrorCategory]::NotSpecified,
$null
)
$err.ErrorDetails = "Failed to get logon session details for current process: $($_.Exception.Message)"
$PSCmdlet.WriteError($err)
}
finally {
if ($currentToken) { $currentToken.Dispose() }
}
return
}
if ($PSCmdlet.ParameterSetName -eq "LogonId" -and -not $LogonId) {
$LogonId = [LSA.LogonSession]::LsaEnumerateLogonSessions()
}
if ($PSCmdlet.ParameterSetName -eq "ProcessId") {
$LogonId = foreach ($process in $ProcessId) {
try {
$procHandle = [LSA.AccessToken]::OpenProcess(
$process,
[LSA.ProcessAccessRights]::QueryInformation,
$false)
$procToken = [LSA.AccessToken]::OpenProcessToken(
$procHandle,
[System.Security.Principal.TokenAccessLevels]::Query)
[LSA.AccessToken]::GetTokenStatistics($procToken).AuthenticationId
}
catch {
$err = [System.Management.Automation.ErrorRecord]::new(
$_.Exception,
"OpenByProcessId",
[System.Management.Automation.ErrorCategory]::NotSpecified,
$null
)
$err.ErrorDetails = "Failed to get logon id for process $($process): $($_.Exception.Message)"
$PSCmdlet.WriteError($err)
}
finally {
if ($procHandle) { $procHandle.Dispose() }
if ($procToken) { $procToken.Dispose() }
}
}
}
foreach ($logon in $LogonId) {
try {
[LSA.LogonSession]::LsaGetLogonSessionData($logon)
}
catch {
$err = [System.Management.Automation.ErrorRecord]::new(
$_.Exception,
"GetLogonSessionData",
[System.Management.Automation.ErrorCategory]::NotSpecified,
$null
)
$err.ErrorDetails = "Failed to get logon session data for $($logon): $($_.Exception.Message)"
$PSCmdlet.WriteError($err)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment