1

我知道我可以通过运行“klist.exe”并解析输出来获得所需的信息,但我想知道是否有 Windows/C#/Powershell API 来获取有关 Windows 服务器上缓存的 Kerberos 票证的信息。

4

2 回答 2

3

Microsoft 已经为此提供了一组脚本。所以,你不必从头开始写这个。查看和清除缓存的 Kerberos 票证,是的,他们有 klist。否则,你最终会试图利用……</p>

[System.Security.Principal.WindowsIdentity]

…然后进行 SID 翻译等,否则您最终会在此问答中进行相同的讨论。

如何以编程方式清除 Kerberos 票证缓存

或者利用这些资源并根据需要进行调整。

Kerberos 模块 该模块提供对 Kerberos 票证缓存的访问权限。它可以读取和清除当前登录会话的票证。

Kerberos 票证的托管代码验证器

Kerberos.NET 的重点是使 Kerberos 在这种情况下更易于使用。这是通过删除对 Windows 的任何硬依赖并将所有票证处理转移到应用程序本身来完成的。这当然意味着您不需要应用程序位于已加入域的计算机上,而且它可能也不需要位于 Windows 上。

Install-Package Kerberos.NET

使用库

票证身份验证分两个阶段进行。第一阶段通过具有 KerberosValidator 的默认实现的 IKerberosValidator 验证票证的正确性。第二阶段涉及将票证转换为可用的 ClaimsIdentity,这发生在 KerberosAuthenticator 中。

最简单的开始方法是创建一个新的 KerberosAuthenticator 并调用 Authenticate。如果您需要调整转换的行为,可以通过重写 ConvertTicket(DecryptedData data) 方法来实现。

var authenticator = new KerberosAuthenticator(new KeyTable(File.ReadAllBytes("sample.keytab")));

var identity = authenticator.Authenticate("YIIHCAYGKwYBBQUCoIIG...");

Assert.IsNotNull(identity);

var name = identity.Name;

Assert.IsFalse(string.IsNullOrWhitespace(name));

请注意,身份验证器的构造函数参数是 KeyTable。KeyTable 是用于在其他平台上存储密钥的常用格式。您可以使用由 ktpass 之类的工具创建的文件,也可以在实例化期间只传递一个 KerberosKey,它会产生相同的效果。

列出所有缓存的 Kerberos 票证

在域中对身份验证进行管理或故障排除时,有时您需要知道用户和服务的票证是否缓存在计算机上。此脚本将计算机上所有用户缓存的票证导出到文本文件以供查看。

下载: GetKerbTix.ps1

清除所有 Kerberos 票证

在某些情况下,管理员可能想要清除服务器上缓存的 Kerberos 票证。例如,用户 Bob 离开了公司。在这种情况下,您可以运行此脚本来清除计算机上所有会话的所有缓存 Kerberos 票证和 TGT。

下载: PurgeAllKerbTickets.ps1

#************************************************ 
# GetKerbTix.ps1 
# Version 1.0 
# Date: 6-11-2014 
# Author: Tim Springston [MSFT] 
# Description: On a specific computer the script is ran on,  
#  this script finds all logon sessions which have Kerberos 
#     tickets cached and enumerates the tickets and any ticket granting tickets. 
# The tickets may be from remote or interactive users and may be  
#  any logon type session (network, batch, interactive, remote interactive...). 
# This script will run on Windows Server 2008/Vista and later. 
#************************************************ 
cls 
$FormatEnumerationLimit = -1 
$ComputerName = $env:COMPUTERNAME 
$UserName = [Security.Principal.WindowsIdentity]::GetCurrent().name 
$ComputerDomain = [System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain().name 
$Date = Get-Date 


#Prepare an output file to place info into. 
$ExportFile = "C:\windows\temp\" + $ComputerName + "_CachedKerberosTickets.txt" 
"Cached Kerberos Tickets" | Out-File $ExportFile -Encoding utf8 
"Logged on User:$UserName" | Out-File $ExportFile -Append -Encoding utf8 
"Computer name: $ComputerName" | Out-File $ExportFile -Append -Encoding utf8 
"Computer Domain: $ComputerDomain" | Out-File $ExportFile -Append -Encoding utf8 
"Date: $Date" | Out-File $ExportFile -Append -Encoding utf8 
"************************************" | Out-File $ExportFile -Append -Encoding utf8 

function GetKerbSessions 
    { 
    $Sessions = @() 
    $WMILogonSessions = gwmi win32_LogonSession 
    foreach ($WMILogonSession in $WMILogonSessions) 
        { 
        $LUID = [Convert]::ToString($WMILogonSession.LogonID, 16) 
        $LUID = '0x' + $LUID 
        $Sessions += $LUID 
        } 
    return $sessions 
    } 

function GetKerbSessionInfo 
    { 
    $OS = gwmi win32_operatingsystem 
    $sessions = New-Object PSObject 
    if ($OS.Buildnumber -ge 9200) 
        { 
        $KlistSessions = klist sessions 
        $Counter = 0 

        foreach ($item in $KlistSessions) 
            { 
            if ($item -match "^\[.*\]") 
                { 
                $LogonId = $item.split(' ')[3] 
                $LogonId = $LogonId.Replace('0:','') 
                $Identity = $item.split(' ')[4] 
                $Token5 = $item.Split(' ')[5] 
                $AuthnMethod = $Token5.Split(':')[0] 
                $LogonType = $Token5.Split(':')[1] 
                $Session = New-Object PSObject 
                Add-Member -InputObject $Session -MemberType NoteProperty -Name "SessionID" -Value $LogonId 
                Add-Member -InputObject $Session -MemberType NoteProperty -Name "Identity" -Value $Identity 
                Add-Member -InputObject $Session -MemberType NoteProperty -Name "Authentication Method" -Value $AuthnMethod             
                Add-Member -InputObject $Session -MemberType NoteProperty -Name "Logon Type" -Value $LogonType 

                Add-Member -InputObject $sessions -MemberType NoteProperty -Name $LogonId -Value $Session 
                $Session = $null 
                } 
            } 
        } 
    if ($OS.Buildnumber -lt 9200) 
        { 
        $WMILogonSessions = gwmi win32_LogonSession 
        foreach ($WMILogonSession in $WMILogonSessions) 
            { 
            $LUID = [Convert]::ToString($WMILogonSession.LogonID, 16) 
            $LUID = '0x' + $LUID 
            $Session = New-Object PSObject 
            Add-Member -InputObject $Session -MemberType NoteProperty -Name "SessionID" -Value $LUID 
            Add-Member -InputObject $Session -MemberType NoteProperty -Name "Identity" -Value "Not available" 
            Add-Member -InputObject $Session -MemberType NoteProperty -Name "Authentication Method" -Value $WMILogonSession.AuthenticationPackage         
            Add-Member -InputObject $Session -MemberType NoteProperty -Name "Logon Type" -Value $WMILogonSession.LogonType 

            Add-Member -InputObject $sessions -MemberType NoteProperty -Name $LUID -Value $Session 
            $Session = $null 
            } 
        } 
    return $sessions 
    } 

function ReturnSessionTGTs 
    { 
    param ($SessionID = $null) 
    if ($SessionID -eq $null) 
        { 
        $RawTGT =  klist.exe tgt 
        } 
        else 
            { 
            $RawTGT =  klist.exe tgt -li $sessionID 
            } 
    $TGT = @() 
    foreach ($Line in $RawTGT) 
        { 
        if ($Line.length -ge 1) 
            { 
            $TGT += $Line 
            } 
        } 
    if ($TGT -contains 'Error calling API LsaCallAuthenticationPackage (Ticket Granting Ticket substatus): 1312') 
        {$TGT = 'No ticket granting ticket cached in session.'} 
    return $TGT 
    }     

function ReturnSessionTickets  
    { 
    param ($SessionID = $null) 
    $OS = gwmi win32_operatingsystem 
    if ($SessionID -eq $null) 
        { 
        $TicketsArray =  klist.exe tickets 
        } 
        else 
            { 
            $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) 

            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 "KerbTicket Encryption Type" -Value $KerbTicketEType 
            Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "Ticket Flags" -Value $TickFlags 
            Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "Start Time" -Value $StartTime 
            Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "End Time" -Value $EndTime 
            Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "Renew Time" -Value $RenewTime 
            Add-Member -InputObject $Ticket -MemberType NoteProperty -Name "Session Key Type" -Value $SessionKey 

            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 
                } 
            Add-Member -InputObject $TicketsObject -MemberType NoteProperty -Name $TicketNumber -Value $Ticket 
            $Ticket = $null 
            } 
        $Counter++ 


        } 
    return $TicketsObject 
    }     

$OS = gwmi win32_operatingsystem 
$sessions = getkerbsessions 
$sessioninfo = GetKerbSessionInfo 
foreach ($Session in $sessions) 
{     
    #Get Session details as well 
    $currentsessioninfo = $sessioninfo.$session 
    $ID = $currentsessioninfo.identity 
    $SessionID = $currentsessioninfo.SessionID 
    $LogonType = $currentsessioninfo.'Logon Type' 
    $AuthMethod = $currentsessioninfo.'Authentication Method' 
    if ($OS.Buildnumber -lt 9200) 
        { 
        Write-Host "Kerberos Tickets for LogonID $SessionID" 
        "Kerberos Tickets for LogonID $SessionID" | Out-File $ExportFile -Append -Encoding utf8 
        } 
        else 
        { 
        Write-Host "Kerberos Tickets for $ID" 
        "Kerberos Tickets for $ID" | Out-File $ExportFile -Append -Encoding utf8 
        } 
    Write-Host "*****************************" 
     "*****************************" | Out-File $ExportFile -Append -Encoding utf8 
    Write-Host "Logon Type: $LogonType" 
    "Logon Type: $LogonType" | Out-File $ExportFile -Append -Encoding utf8 
    Write-host "Session ID: $SessionID" 
    "Session ID: $SessionID" | Out-File $ExportFile -Append -Encoding utf8 
    Write-host "Auth Method: $AuthMethod" 
    "Auth Method: $AuthMethod" | Out-File $ExportFile -Append -Encoding utf8 
    $SessionTickets = ReturnSessionTickets $Session 


    $TGT = ReturnSessionTGTs $SessionID 
    $TGT | FL * 
    $TGT | Out-File $ExportFile -Append -Encoding utf8 

    if ($SessionTickets -notmatch 'Ticket') 
        { 
        Write-Host "Session TGT: No tickets for this session in cache." 
        "Session TGT: No tickets for this session in cache." | Out-File $ExportFile -Append -Encoding utf8 
        } 
        else 
        { 
        $SessionTickets | FL * 
        $SessionTickets    | Out-File $ExportFile -Append -Encoding utf8 
        } 
    Write-Host "`n" 
     "`n" | Out-File $ExportFile -Append -Encoding utf8 

} 


#************************************************
# PurgeAllKerbTickets.ps1
# Version 1.0
# Date: 6-12-2014
# Author: Tim Springston [MSFT]
# Description: On a specific computer the script is ran on, 
#  this script finds all logon sessions which have Kerberos
#  tickets cached and for each session purges the ticket granting
#   tickets and the tickets using klist.exe.
#************************************************
cls

function GetKerbSessions
    {
    $Sessions = @()
    $WMILogonSessions = gwmi win32_LogonSession
    foreach ($WMILogonSession in $WMILogonSessions)
        {
        $LUID = [Convert]::ToString($WMILogonSession.LogonID, 16)
        $LUID = '0x' + $LUID
        $Sessions += $LUID
        }
    return $sessions
    }

Write-Host "WARNING: This script will purge all cached Kerberos tickets on the local computer for all sessions (whether interactive, network or other sessions)."  -backgroundcolor Red 
Write-Host "In a well-connected environment clients will request and obtain Kerberos tickets on demand without interruption. If not well-connected to a domain controller (remote network) then further network resource authentication may fail or use NTLM if tickets are purged." -BackgroundColor red
Write-Host "Confirm whether to purge by entering YES"
$Response = Read-Host

if ($Response -match 'YES')
    {
    $sessions = GetKerbSessions

    foreach ($Session in $sessions)
        {
        $PurgedTix = klist.exe -li $Session purge
        }
    Write-Host "All tickets purged!" -backgroundcolor green
    }
    else
        {
        Write-Host "Confirmation not received. NOT purging tickets." -backgroundcolor yellow
        }
于 2019-02-16T08:30:05.163 回答
0

到目前为止,我能够找到 klist.exe 的源代码,并且“LsaCa​​llAuthenticationPackage”似乎是在 Windows 中与 Kerberos 缓存进行通信的方式:

https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/security/authorization/klist/KList.c

Status = LsaCallAuthenticationPackage(
            LogonHandle,
            PackageId,
            &CacheRequest,
            sizeof(CacheRequest),
            (PVOID *) &CacheResponse,
            &ResponseSize,
            &SubStatus
            );
if (!SEC_SUCCESS(Status) || !SEC_SUCCESS(SubStatus))
{
    ShowNTError("LsaCallAuthenticationPackage", Status);
    printf("Substatus: 0x%x\n",SubStatus);
    return FALSE;
}

printf("\nCached Tickets: (%lu)\n", CacheResponse->CountOfTickets);
for (Index = 0; Index < CacheResponse->CountOfTickets ; Index++ )
{
    printf("\n   Server: %wZ@%wZ\n",
        &CacheResponse->Tickets[Index].ServerName,
        &CacheResponse->Tickets[Index].RealmName);
于 2019-02-19T22:57:27.277 回答