Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Checks the username of logged in users to the Kerberos Tickets. Whilst not the best it is a potential local based detection for things like Pass-the-Ticket, Golden Ticket type attacks
<#
This is comprised of 3 functions:
1. Get-Sessions: Get Session information from WMI.
Reused some code from https://github.com/tmmtsmith/Powershell/blob/master/Get-LoggedOnUsers.ps1 for the regex matching
2. Return-SessionTickets: Retrieves Kerberos Session Tickets. This is a klist wrapper to return PSCustomObjects
This code is mostly all from the GetKerbTix.ps1 script in the Technet Script Center
https://gallery.technet.microsoft.com/scriptcenter/List-All-Cached-Kerberos-5ba41829
3. Invoke-PtTCheck: Perform username matching to see if the username logged on has different username to Kerberos tickets registered for their session
This is not by any means a full detection for Pass-the-Ticket or Golden Ticket but can just show 1 simple way
in which a blue team could go about checking for something like this on a compromised client.
USAGE
1. Dot source the script
PS> . Invoke-PtTCheck.ps1
2. Run Invoke-PtTCheck
#>
function Get-Sessions
{
param
(
[parameter(Mandatory = $false)]
[String[]]
$computerName = "localhost"
)
Write-Verbose -Message "Executing Win32_LoggedOnUser on $Computer"
$logon_users = Get-WmiObject win32_loggedonuser -ComputerName $ComputerName
if ($logon_users)
{
Write-Verbose -Message "Found $($logon_users.count) Logon Sessions on $Computer"
foreach ($Logon_User in $Logon_Users)
{
# Ensure Antecedent Matches the Domain and Name format expected. PowerShell Automatically stores matches in $Matches variable.
$Logon_User.antecedent -match $regexa | Out-Null
#Create Domain\Username format
$username = $matches[1] + "\" + $matches[2]
# Ensure Dependent matches the expected logonID format
$Logon_User.dependent -match $regexd | Out-Null
# Extract Logon ID from matches
$session = $matches[1]
# Put Logon ID in the Session Hex Format which is used by klist
$sessionHex = ('0x{0:X}' -f [int]$session)
# Create and output objects
$Object = New-Object -TypeName psobject
$Object | Add-Member -MemberType NoteProperty -Name "Session" -Value $sessionHex
$Object | Add-Member -MemberType NoteProperty -Name "Username" -Value $username
$Object
}
}
}
function Return-SessionTickets
{
param
(
[parameter(Mandatory = $false)]
$SessionID = $null,
[Switch]$TimeOutput
)
$OS = Get-WmiObject win32_operatingsystem
if ($SessionID -eq $null)
{
$TicketsArray = klist.exe tickets
}
else
{
$TicketsArray = klist.exe tickets -li $sessionID
if ($TicketsArray -like "*Error calling API LsaCallAuthenticationPackage*")
{
$SessionID = "0:$sessionID"
$TicketsArray = klist.exe tickets -li $sessionID
}
}
$Counter = 0
$TicketsObject = New-Object PSObject
foreach ($line in $TicketsArray)
{
if ($line -match "^#\d")
{
$Ticket = New-Object PSObject
$Number = $Line.Split('>')[0]
$Line1 = $Line.Split('>')[1]
$TicketNumber = "Ticket " + $Number
$Client = $Line1; $Client = $Client.Replace('Client:', ''); $Client = $Client.Substring(2)
$Server = $TicketsArray[$Counter + 1]; $Server = $Server.Replace('Server:', ''); $Server = $Server.substring(2)
$KerbTicketEType = $TicketsArray[$Counter + 2]; $KerbTicketEType = $KerbTicketEType.Replace('KerbTicket Encryption Type:', ''); $KerbTicketEType = $KerbTicketEType.substring(2)
$TickFlags = $TicketsArray[$Counter + 3]; $TickFlags = $TickFlags.Replace('Ticket Flags', ''); $TickFlags = $TickFlags.substring(2)
$StartTime = $TicketsArray[$Counter + 4]; $StartTime = $StartTime.Replace('Start Time:', ''); $StartTime = $StartTime.substring(2)
$EndTime = $TicketsArray[$Counter + 5]; $EndTime = $EndTime.Replace('End Time:', ''); $EndTime = $EndTime.substring(4)
$RenewTime = $TicketsArray[$Counter + 6]; $RenewTime = $RenewTime.Replace('Renew Time:', ''); $RenewTime = $RenewTime.substring(2)
$SessionKey = $TicketsArray[$Counter + 7]; $SessionKey = $SessionKey.Replace('Session Key Type:', ''); $SessionKey = $SessionKey.substring(2)
# Convert Start and End Times to actual dates
[datetime]$StartTime_Obj = $StartTime.TrimEnd('(local)')
[datetime]$EndTime_Obj = $EndTime.TrimEnd('(local)')
[datetime]$RenewTime_Obj = $RenewTime.TrimEnd('(local)')
# Calculate the Service Ticket Lifetime
$Ticket_Lifetime = New-TimeSpan -Start $StartTime_Obj -End $EndTime_Obj
$Renew_Lifetime = New-TimeSpan -Start $StartTime_Obj -End $RenewTime_Obj
# Get Service From SPN
$Service = $Server.split('/')[0]
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "TicketNumber" -Value $TicketNumber
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "Client" -Value $Client
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "Server" -Value $Server
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "KerbTicketEncryptionType" -Value $KerbTicketEType
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "TicketFlags" -Value $TickFlags
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "StartTime" -Value $StartTime_Obj
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "EndTime" -Value $EndTime_Obj
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "RenewTime" -Value $RenewTime_Obj
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "SessionKeyType" -Value $SessionKey
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "TicketLifetime" -Value $Ticket_Lifetime
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "Service" -Value $Service
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "RenewLifetime" -Value $Renew_Lifetime
if ($OS.BuildNumber -ge 9200)
{
$CacheFlags = $TicketsArray[$Counter + 8]; $CacheFlags = $CacheFlags.Replace('Cache Flags:', ''); $CacheFlags = $CacheFlags.substring(2)
$KDCCalled = $TicketsArray[$Counter + 9]; $KDCCalled = $KDCCalled.Replace('Kdc Called:', ''); $KDCCalled = $KDCCalled.substring(2)
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "Cache Flags" -Value $CacheFlags
Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "KDC Called" -Value $KDCCalled
}
[array]$Tickets += $Ticket
}
$Counter++
}
If ($TimeOutput)
{
return $Tickets | select-object Client, Service, StartTime, EndTime, RenewTime, Ticketlifetime, RenewLifetime | Sort-Object -Descending TicketLifetime | Format-Table
}
else
{
return $Tickets
}
}
function Invoke-PtTCheck
{
param
(
[parameter(Mandatory = $false)]
[string[]]$ComputerName = "localhost" # For when / if it can support multiple hosts
)
BEGIN
{
# Regex Matches to get session info
$regexa = '.+Domain="(.+)",Name="(.+)"$'
$regexd = '.+LogonId="(\d+)"$'
}
PROCESS
{
foreach ($Computer in $ComputerName)
{
[pscustomobject]$sessions = Get-Sessions -computerName $Computer
foreach ($Session in $sessions)
{
# Use Wrapper Function for Klist to search for tickets for the use
$Session_Tickets = Return-SessionTickets -sessionID $Session.Session
if ($Session_Tickets)
{
# Get Unique Client (Username) from Ticket FORMAT = Username @ FQDN Domain
$Unique_Username_for_session = $Session_Tickets.Client | Select-Object -Unique
$Unique_Username_for_session_Formatted = $Unique_Username_for_session.split(" ")[0]
Write-Verbose -Message "Unique Username in $sessionHex`: $Unique_Username_for_session_Formatted"
# Due to weird formatting and potential for different netbios vs full dns name
# of domain just check to see if the username in the session ticket is on the right of the username field
if ($Session.username.split('\')[-1] -eq $Unique_Username_for_session_Formatted)
{
$Session | Add-Member -MemberType NoteProperty -Name "IsPtT" -Value $False
$Session | Add-Member -MemberType NoteProperty -Name "PtTUser" -Value $null
}
elseif ($Session.Username.split('\')[0] -eq $Unique_Username_for_session_Formatted.trimend('$'))
{
# This is to match computer name in the domain field to the computername in Kerberos tickets for SYSTEM / Local Service / Network Service Services
$Session | Add-Member -MemberType NoteProperty -Name "IsPtT" -Value $False
$Session | Add-Member -MemberType NoteProperty -Name "PtTUser" -Value $null
}
else
{
$Session | Add-Member -MemberType NoteProperty -Name "IsPtT" -Value $true
$Session | Add-Member -MemberType NoteProperty -Name "PtTUser" -Value $Unique_Username_for_session_Formatted
}
}
else
{
Write-Verbose -Message "No session tickets for user: $($Session.Username)"
$Session | Add-Member -MemberType NoteProperty -Name "IsPtT" -Value $false
$Session | Add-Member -MemberType NoteProperty -Name "PtTUser" -Value $null
}
[array] $sessionOutput += $Session
}
}
}
END
{
$sessionOutput
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment