Skip to content

Commit 392f87d

Browse files
authored
Merge pull request #20401 from zeroSteiner/feat/mod/ldap/gmsa-secrets
Add gMSA Secret Extraction From LDAP
2 parents 679c74f + 54c5cda commit 392f87d

File tree

4 files changed

+211
-127
lines changed

4 files changed

+211
-127
lines changed

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

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,29 @@ module Formatter
99
# @param cred [credClass] A credential from framework.db
1010
# @return [String] The hash in jtr format or nil on no match.
1111
def self.hash_to_jtr(cred)
12-
case cred.private.type
13-
when 'Metasploit::Credential::NTLMHash'
14-
return "#{cred.public.username}:#{cred.id}:#{cred.private.data}:::#{cred.id}"
15-
when 'Metasploit::Credential::PostgresMD5'
16-
if cred.private.jtr_format =~ /postgres|raw-md5/
12+
params_to_jtr(
13+
(cred.public.nil? ? '' : cred.public.username),
14+
cred.private.data,
15+
cred.class.model_name.element.to_sym,
16+
format: cred.private.jtr_format,
17+
db_id: cred.id
18+
)
19+
end
20+
21+
def self.params_to_jtr(username, private_data, private_type, format: nil, db_id: nil)
22+
case private_type
23+
when :ntlm_hash
24+
return "#{username}:#{db_id}:#{private_data}:::#{db_id}"
25+
when :postgres_md5
26+
if format =~ /postgres|raw-md5/
1727
# john --list=subformats | grep 'PostgreSQL MD5'
1828
# UserFormat = dynamic_1034 type = dynamic_1034: md5($p.$u) (PostgreSQL MD5)
19-
hash_string = cred.private.data
29+
hash_string = private_data
2030
hash_string.gsub!(/^md5/, '')
21-
return "#{cred.public.username}:$dynamic_1034$#{hash_string}:#{cred.id}:"
31+
return "#{username}:$dynamic_1034$#{hash_string}:#{db_id}:"
2232
end
23-
when 'Metasploit::Credential::NonreplayableHash'
24-
case cred.private.jtr_format
33+
when :nonreplayable_hash
34+
case format
2535
# oracle 11+ password hash descriptions:
2636
# this password is stored as a long ascii string with several sections
2737
# https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/changes-in-oracle-database-12c-password-hashes/
@@ -40,46 +50,46 @@ def self.hash_to_jtr(cred)
4050
# T: = 160 characters
4151
# PBKDF2-based SHA512 hash specific to 12C (12.1.0.2+)
4252
when /raw-sha1|oracle11/ # oracle 11
43-
if cred.private.data =~ /S:([\dA-F]{60})/ # oracle 11
44-
return "#{cred.public.username}:#{Regexp.last_match(1)}:#{cred.id}:"
53+
if private_data =~ /S:([\dA-F]{60})/ # oracle 11
54+
return "#{username}:#{Regexp.last_match(1)}:#{db_id}:"
4555
end
4656
when /oracle12c/
47-
if cred.private.data =~ /T:([\dA-F]{160})/ # oracle 12c
48-
return "#{cred.public.username}:$oracle12c$#{Regexp.last_match(1).downcase}:#{cred.id}:"
57+
if private_data =~ /T:([\dA-F]{160})/ # oracle 12c
58+
return "#{username}:$oracle12c$#{Regexp.last_match(1).downcase}:#{db_id}:"
4959
end
5060
when /dynamic_1506/
51-
if cred.private.data =~ /H:([\dA-F]{32})/ # oracle 11
52-
return "#{cred.public.username.upcase}:$dynamic_1506$#{Regexp.last_match(1)}:#{cred.id}:"
61+
if private_data =~ /H:([\dA-F]{32})/ # oracle 11
62+
return "#{username.upcase}:$dynamic_1506$#{Regexp.last_match(1)}:#{db_id}:"
5363
end
5464
when /oracle/ # oracle
55-
if cred.private.jtr_format.start_with?('des') # 'des,oracle', not oracle11/12c
56-
return "#{cred.public.username}:O$#{cred.public.username}##{cred.private.data}:#{cred.id}:"
65+
if format.start_with?('des') # 'des,oracle', not oracle11/12c
66+
return "#{username}:O$#{username}##{private_data}:#{db_id}:"
5767
end
5868
when /md5|des|bsdi|crypt|bf|sha256|sha512|xsha512/
5969
# md5(crypt), des(crypt), b(crypt), sha256(crypt), sha512(crypt), xsha512
60-
return "#{cred.public.username}:#{cred.private.data}:::::#{cred.id}:"
70+
return "#{username}:#{private_data}:::::#{db_id}:"
6171
when /xsha/
6272
# xsha512
63-
return "#{cred.public.username}:#{cred.private.data.upcase}:::::#{cred.id}:"
73+
return "#{username}:#{private_data.upcase}:::::#{db_id}:"
6474
when /netntlm/
65-
return "#{cred.private.data}::::::#{cred.id}:"
75+
return "#{private_data}::::::#{db_id}:"
6676
when /qnx/
6777
# https://moar.so/blog/qnx-password-hash-formats.html
68-
hash = cred.private.data.end_with?(':0:0') ? cred.private.data : "#{cred.private.data}:0:0"
69-
return "#{cred.public.username}:#{hash}"
78+
hash = private_data.end_with?(':0:0') ? private_data : "#{private_data}:0:0"
79+
return "#{username}:#{hash}"
7080
when /Raw-MD5u/
7181
# This is just md5(unicode($p)), where $p is the password.
7282
# Avira uses to store their passwords, there may be other apps that also use this though.
7383
# The trailing : shows an empty salt. This is because hashcat only has one unicode hash
7484
# format which is compatible, type 30, but that is listed as md5(utf16le($pass).$salt)
7585
# with a sample hash of b31d032cfdcf47a399990a71e43c5d2a:144816. So this just outputs
7686
# The hash as *hash*: so that it is both JTR and hashcat compatible
77-
return "#{cred.private.data}:"
87+
return "#{private_data}:"
7888
when /vnc/
7989
# add a beginning * if one is missing
80-
return "$vnc$#{cred.private.data.start_with?('*') ? cred.private.data.upcase : "*#{cred.private.data.upcase}"}"
90+
return "$vnc$#{private_data.start_with?('*') ? private_data.upcase : "*#{private_data.upcase}"}"
8191
when /^(krb5.|timeroast$)/
82-
return cred.private.data
92+
return private_data
8393
else
8494
# /mysql|mysql-sha1/
8595
# /mssql|mssql05|mssql12/
@@ -93,9 +103,10 @@ def self.hash_to_jtr(cred)
93103
# /mscash2/
94104
# This also handles *other* type credentials which aren't guaranteed to have a public
95105

96-
return "#{cred.public.nil? ? ' ' : cred.public.username}:#{cred.private.data}:#{cred.id}:"
106+
return "#{username}:#{private_data}:#{db_id}:"
97107
end
98108
end
109+
99110
nil
100111
end
101112

lib/msf/util/windows_crypto_helpers.rb

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ module Msf
22
module Util
33
module WindowsCryptoHelpers
44

5+
EMPTY_LM = "\xaa\xd3\xb4\x35\xb5\x14\x04\xee\xaa\xd3\xb4\x35\xb5\x14\x04\xee".b
6+
EMPTY_NT = "\x31\xd6\xcf\xe0\xd1\x6a\xe9\x31\xb7\x3c\x59\xd7\xe0\xc0\x89\xc0".b
7+
58
#class Error < RuntimeError; end
69
#class Unknown < Error; end
710

@@ -200,28 +203,26 @@ def decrypt_user_hash(rid, hboot_key, enc_hash, pass, default)
200203
def decrypt_user_key(hboot_key, user_v, rid)
201204
sam_lmpass = "LMPASSWORD\x00"
202205
sam_ntpass = "NTPASSWORD\x00"
203-
sam_empty_lm = ['aad3b435b51404eeaad3b435b51404ee'].pack('H*')
204-
sam_empty_nt = ['31d6cfe0d16ae931b73c59d7e0c089c0'].pack('H*')
205206

206207
# TODO: use a proper structure for V data, instead of unpacking directly
207208
hashlm_off = user_v[0x9c, 4]&.unpack('V')&.first
208209
hashlm_len = user_v[0xa0, 4]&.unpack('V')&.first
209210
if hashlm_off && hashlm_len
210211
hashlm_enc = user_v[hashlm_off + 0xcc, hashlm_len]
211-
hashlm = decrypt_user_hash(rid, hboot_key, hashlm_enc, sam_lmpass, sam_empty_lm)
212+
hashlm = decrypt_user_hash(rid, hboot_key, hashlm_enc, sam_lmpass, EMPTY_LM)
212213
else
213214
elog('decrypt_user_key: Unable to extract LM hash, using empty LM hash instead')
214-
hashlm = sam_empty_lm
215+
hashlm = EMPTY_LM
215216
end
216217

217218
hashnt_off = user_v[0xa8, 4]&.unpack('V')&.first
218219
hashnt_len = user_v[0xac, 4]&.unpack('V')&.first
219220
if hashnt_off && hashnt_len
220221
hashnt_enc = user_v[hashnt_off + 0xcc, hashnt_len]
221-
hashnt = decrypt_user_hash(rid, hboot_key, hashnt_enc, sam_ntpass, sam_empty_nt)
222+
hashnt = decrypt_user_hash(rid, hboot_key, hashnt_enc, sam_ntpass, EMPTY_NT)
222223
else
223224
elog('decrypt_user_key: Unable to extract NT hash, using empty NT hash instead')
224-
hashnt = sam_empty_nt
225+
hashnt = EMPTY_NT
225226
end
226227

227228
[hashnt, hashlm]

0 commit comments

Comments
 (0)