Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add namespace to identify.rb #17135

Merged
merged 3 commits into from
Oct 19, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions lib/metasploit/framework/hashes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
module Metasploit
module Framework
# This module contains utilities for hashes, including one to identify them
# Resource list:
# https://code.google.com/archive/p/hash-identifier/
# https://github.com/psypanda/hashID
# https://hashcat.net/wiki/doku.php?id=example_hashes
# http://pentestmonkey.net/cheat-sheet/john-the-ripper-hash-formats
# https://openwall.info/wiki/john/sample-hashes
# QNX formats -> https://moar.so/blog/qnx-password-hash-formats.html
# rubocop:disable Metrics/ModuleLength
module Hashes
JTR_NTLMV1 = 'netntlm'.freeze
JTR_NTLMV2 = 'netntlmv2'.freeze
def self.identify_hash(hash)
# @param [str] a string of a hashed password
# @return [String] the jtr type or empty string on no match
hash = hash.to_s.strip
case
# operating systems
when hash.start_with?('$1$') && hash.length == 34
return 'md5'
when hash.start_with?('$2$') && hash.length == 59,
hash.start_with?('$2a$') && hash.length == 60,
hash.start_with?('$2b$') && hash.length == 60,
hash.start_with?('$2x$') && hash.length == 60,
hash.start_with?('$2y$') && hash.length == 60
return 'bf' # bcrypt
when hash.start_with?('$5$') && hash.split('$').last.length == 43
# we dont check full length since it may have 'rounds=' in the [1] area or not with an arbitrary length number
return 'sha256,crypt'
when hash.start_with?('$6$') && hash.split('$').last.length == 86
# we dont check full length since it may have 'rounds=' in the [1] area or not with an arbitrary length number
return 'sha512,crypt'
when hash.start_with?('@S@') && hash.length == 148
return 'qnx,sha512'
when hash.start_with?('@s@') && hash.length == 84
return 'qnx,sha256'
when hash.start_with?('@m@') && hash.length == 52
return 'qnx,md5'
when hash.start_with?('$y$') && hash.split('$').last.length == 43
return 'yescrypt'
when hash.start_with?('_') && hash.length == 20
return 'des,bsdi,crypt'
when hash =~ %r{^[./\dA-Za-z]{13}$} # hash.length == 13
return 'des,crypt'
when hash =~ /^\$dynamic_82\$[\da-f]{128}\$HEX\$[\da-f]{32}$/ # jtr vmware ldap https://github.com/rapid7/metasploit-framework/pull/13865#issuecomment-660718108
return 'dynamic_82'
when hash.start_with?(/{SSHA}/i)
return 'ssha'
when hash.start_with?(/{SHA512}/i)
return 'raw-sha512'
when hash.start_with?(/{SHA256}/i)
return 'raw-sha256'
when hash.start_with?(/{SHA}/i)
return 'raw-sha1'
when hash.start_with?(/{MD5}/i)
return 'raw-md5'
when hash.start_with?(/{SMD5}/i)
return 'smd5'
when hash.start_with?(/{SSHA256}/i)
return 'ssha256'
when hash.start_with?(/{SSHA512}/i)
return 'ssha512'
# windows
when hash.length == 65 && hash =~ /^[\da-fA-F]{32}:[\da-fA-F]{32}$/ && hash.split(':').first.upcase == 'AAD3B435B51404EEAAD3B435B51404EE'
return 'nt'
when hash.length == 65 && hash =~ /^[\da-fA-F]{32}:[\da-fA-F]{32}$/
return 'lm'
# OSX
when hash.start_with?('$ml$') && hash.split('$').last.length == 256
return 'pbkdf2-hmac-sha512,osx' # 10.8+
when hash =~ /^[\da-fA-F]{48}$/ # hash.length == 48
return 'xsha,osx' # 10.4-10.6
# databases
when hash.start_with?('0x0100') && hash.length == 54
return 'mssql05'
when hash.start_with?('0x0100') && hash.length == 94
return 'mssql'
when hash.start_with?('0x0200') && hash.length == 142
return 'mssql12'
when hash =~ /^[\da-f]{16}$/ # hash.length == 16
return 'mysql' # mysql323 (pre 4.1)
when hash.start_with?('*') && hash.length == 41
return 'mysql-sha1' # mysql 4.1+
when hash.start_with?('md5') && hash.length == 35
return 'postgres'
when hash =~ /^[\da-fA-F]{16}$/
return 'des,oracle' # pre 11g
when hash =~ /^S:[\dA-F]{60}$/
return 'raw-sha1,oracle11'
when hash =~ /^S:[\dA-F]{60};H:[\dA-F]{32};T:[\dA-F]{160}$/
return 'raw-sha1,oracle'
when hash =~ /^H:[\dA-F]{32};T:[\dA-F]{160}$/
return 'pbkdf2,oracle12c'
# webapps
when hash.start_with?('$P$') && hash.length == 34,
hash.start_with?('$H$') && hash.length == 34
return 'phpass' # wordpress, drupal, phpbb3 (H not P)
when hash.start_with?('$ml$') && hash.length == 203
return 'PBKDF2-HMAC-SHA512'
when hash.start_with?('{PKCS5S2}') && hash.length == 73
return 'PBKDF2-HMAC-SHA1'
when hash.start_with?('$B$') && hash.split('$').last.length == 32
return 'mediawiki'
# mobile
when hash =~ /^[A-F0-9]{40}:[a-f0-9]{16}$/
return 'android-sha1'
when hash =~ /^[A-F0-9]{32}:[a-f0-9]{16}$/
return 'android-md5'
# other
when hash =~ /^<\d+@.+?>#\w{32}$/
return 'hmac-md5'
when hash.length == 114 && hash.start_with?('$M$')
return 'F5-Secure-Vault'
when hash =~ /^M\$[[:print:]]+#[\da-fA-F]{32}(?:(?::[[:print:]]*$)|$)/
return 'mscash'
when hash =~ /^\$DCC2\$\d+#[[:print:]]+#[\da-fA-F]{32}(?:(?::[[:print:]]*$)|$)/
return 'mscash2'
when hash =~ /^\*?[\da-fA-F]{32}\*[\da-fA-F]{32}$/
# we accept the beginning star as optional
return 'vnc'
end
''
end
# rubocop:enable Metrics/ModuleLength
end
end
end
126 changes: 0 additions & 126 deletions lib/metasploit/framework/hashes/identify.rb

This file was deleted.

6 changes: 3 additions & 3 deletions lib/msf/core/auxiliary/arista.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: binary -*-

require 'metasploit/framework/hashes/identify'
require 'metasploit/framework/hashes'

module Msf
###
Expand Down Expand Up @@ -123,7 +123,7 @@ def arista_eos_config_eater(thost, tport, config)
cred[:jtr_format] = ''
else
output << " and Hash: #{hash}"
cred[:jtr_format] = identify_hash(hash)
cred[:jtr_format] = Metasploit::Framework::Hashes.identify_hash(hash)
end

cred[:username] = name
Expand Down Expand Up @@ -154,7 +154,7 @@ def arista_eos_config_eater(thost, tport, config)
cred[:jtr_format] = ''
else
output << " with Hash: #{hash}"
cred[:jtr_format] = identify_hash(hash)
cred[:jtr_format] = Metasploit::Framework::Hashes.identify_hash(hash)
end

cred[:private_data] = hash.to_s
Expand Down
8 changes: 4 additions & 4 deletions lib/msf/core/auxiliary/f5.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: binary -*-

require 'metasploit/framework/hashes/identify'
require 'metasploit/framework/hashes'

module Msf
###
Expand Down Expand Up @@ -63,7 +63,7 @@ def f5_config_eater(thost, tport, config, store = true)
shell = result[3].strip
cred = credential_data.dup
cred[:username] = username
cred[:jtr_format] = identify_hash(hash)
cred[:jtr_format] = Metasploit::Framework::Hashes.identify_hash(hash)
cred[:private_data] = hash
create_credential_and_login(cred)
print_good("#{thost}:#{tport} Username '#{username}' with description '#{description}' and shell #{shell} with hash #{hash}")
Expand Down Expand Up @@ -166,7 +166,7 @@ def f5_config_eater(thost, tport, config, store = true)
file = result[2].strip
cred = credential_data.dup
cred[:username] = username
cred[:jtr_format] = identify_hash(hash)
cred[:jtr_format] = Metasploit::Framework::Hashes.identify_hash(hash)
cred[:private_data] = hash
create_credential_and_login(cred)
print_good("#{thost}:#{tport} SSL Key '#{username}' and hash #{hash} for #{file}")
Expand All @@ -185,7 +185,7 @@ def f5_config_eater(thost, tport, config, store = true)
key = result[1].strip
cred = credential_data.dup
cred[:username] = "F5 #{key_type} hash"
cred[:jtr_format] = identify_hash(key) # will come bacy empty
cred[:jtr_format] = Metasploit::Framework::Hashes.identify_hash(key) # will come bacy empty
cred[:private_data] = key
create_credential_and_login(cred)
print_good("#{thost}:#{tport} F5 #{key_type} hash #{key}")
Expand Down
8 changes: 4 additions & 4 deletions lib/msf/core/auxiliary/juniper.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: binary -*-

require 'metasploit/framework/hashes/identify'
require 'metasploit/framework/hashes'

module Msf
###
Expand Down Expand Up @@ -177,7 +177,7 @@ def juniper_junos_config_eater(thost, tport, config)
# }
if /root-authentication\s+\{\s+encrypted-password "(?<root_hash>[^"]+)";/i =~ config
root_hash = root_hash.strip
jtr_format = identify_hash root_hash
jtr_format = Metasploit::Framework::Hashes.identify_hash root_hash

print_good("root password hash: #{root_hash}")
if framework.db.active
Expand All @@ -195,7 +195,7 @@ def juniper_junos_config_eater(thost, tport, config)
user_uid = result[2].strip
user_permission = result[3].strip
user_hash = result[4].strip
jtr_format = identify_hash user_hash
jtr_format = Metasploit::Framework::Hashes.identify_hash user_hash

print_good("User #{user_uid} named #{user_name} in group #{user_permission} found with password hash #{user_hash}.")
next unless framework.db.active
Expand Down Expand Up @@ -251,7 +251,7 @@ def juniper_junos_config_eater(thost, tport, config)
result_block[0].strip.scan(/(?<tacplus_server>[0-9.]{7,15}) secret "(?<hash>[^"]+)";/i).each do |result|
ip = result[0].strip
hash = result[1].strip
jtr_format = identify_hash hash
jtr_format = Metasploit::Framework::Hashes.identify_hash hash
print_good("tacplus server #{ip} with password hash #{hash}")
next unless framework.db.active

Expand Down
1 change: 0 additions & 1 deletion lib/msf/core/auxiliary/ubiquiti.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# -*- coding: binary -*-

require 'metasploit/framework/hashes/identify'
require 'bson'
require 'zip'

Expand Down
4 changes: 2 additions & 2 deletions lib/msf/core/auxiliary/vyos.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: binary -*-

require 'metasploit/framework/hashes/identify'
require 'metasploit/framework/hashes'

module Msf
###
Expand Down Expand Up @@ -101,7 +101,7 @@ def vyos_config_eater(thost, tport, config, store = true)
cred = credential_data.dup
cred[:username] = username
unless hash.start_with?('********') # if not in config mode these are masked
cred[:jtr_format] = identify_hash(hash)
cred[:jtr_format] = Metasploit::Framework::Hashes.identify_hash(hash)
cred[:private_data] = hash
print_hash = " with hash #{hash}"
end
Expand Down
Loading