metasploit-framework/modules/auxiliary/scanner/ssh/ssh_version.rb

279 lines
11 KiB
Ruby

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'recog'
require 'net/ssh/transport/session'
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize
super(
'Name' => 'SSH Version Scanner',
'Description' => 'Detect SSH Version, and the server encryption',
'References' => [
['URL', 'https://en.wikipedia.org/wiki/SecureShell'], # general info
['URL', 'https://datatracker.ietf.org/doc/html/rfc8732#name-deprecated-algorithms'], # deprecation of kex gss-sha1 stuff
['URL', 'https://datatracker.ietf.org/doc/html/draft-ietf-curdle-ssh-kex-sha2-20#page-16'], # diffie-hellman-group-exchange-sha1, diffie-hellman-group1-sha1, rsa1024-sha1
['URL', 'https://datatracker.ietf.org/doc/html/rfc8758#name-iana-considerations'], # arc4 deprecation
['URL', 'https://github.com/net-ssh/net-ssh?tab=readme-ov-file#supported-algorithms'], # a bunch of diff removed things from the ruby lib
['CVE', '2008-5161'] # CBC modes
],
'Author' => [
'Daniel van Eeden <metasploit[at]myname.nl>', # original author
'h00die' # algorithms enhancements
],
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(22),
OptInt.new('TIMEOUT', [true, 'Timeout for the SSH probe', 30]),
OptBool.new('EXTENDED_CHECKS', [true, 'Check for cryptographic issues', true])
],
self.class
)
end
def timeout
datastore['TIMEOUT']
end
def rport
datastore['RPORT']
end
def perform_recog(ident)
table = []
recog_info = []
if /^SSH-\d+\.\d+-(.*)$/ =~ ident
recog_match = Recog::Nizer.match('ssh.banner', ::Regexp.last_match(1))
if recog_match
recog_match.each_pair do |k, v|
next if k == 'matched'
recog_info << "#{k}: #{v}"
end
end
end
return table if recog_info.empty?
recog_info.each do |info|
info = info.split(': ')
table << [info[0], info[1..].join(': ')]
end
table
end
def check_host_key(server_data)
table = []
host_key_checks = {
%w[
ecdsa-sha2-nistp521 ecdsa-sha2-nistp384
ecdsa-sha2-nistp256
] => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#host-keys']
}
server_data[:host_key].each do |host_key|
note = ''
host_key_checks.each do |host_key_check, refs|
host_key_check.each do |bad_key|
next unless host_key.downcase == bad_key
vprint_good("#{target_host} - Host Key Encryption #{host_key} uses a weak elliptic curve and should not be used.")
report_vuln(
host: target_host,
port: rport,
proto: 'tcp',
name: name,
info: "Module #{fullname} confirmed SSH Host Key Encryption #{host_key} is available, but should be deprecated",
refs: refs
)
note = 'Weak elliptic curve'
end
end
table << ['encryption.host_key', host_key, note]
end
table
end
def check_encryption(server_data)
table = []
encryption_checks = {
'arcfour' => ['https://datatracker.ietf.org/doc/html/rfc8758#name-iana-considerations'],
'arcfour128' => ['https://datatracker.ietf.org/doc/html/rfc8758#name-iana-considerations'],
'arcfour256' => ['https://datatracker.ietf.org/doc/html/rfc8758#name-iana-considerations'],
'aes256-cbc' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers', 'CVE-2008-5161'],
'aes192-cbc' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers', 'CVE-2008-5161'],
'aes128-cbc' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers', 'CVE-2008-5161'],
'rijndael-cbc@lysator.liu.se' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers', 'CVE-2008-5161'],
'blowfish-cbc' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers', 'CVE-2008-5161'],
'cast128-cbc' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers', 'CVE-2008-5161'],
'3des-cbc' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers', 'CVE-2008-5161'],
'idea-cbc' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers', 'CVE-2008-5161'],
'twofish-cbc' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers', 'CVE-2008-5161'],
'twofish128-cbc' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers', 'CVE-2008-5161'],
'twofish256-cbc' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers', 'CVE-2008-5161'],
'blowfish-ctr' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers'],
'cast128-ctr' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers'],
'3des-ctr' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers'],
'none' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#encryption-algorithms-ciphers']
}
server_data[:encryption_server].each do |encryption|
note = ''
encryption_checks.each do |bad_enc, refs|
next unless encryption.downcase == bad_enc
vprint_good("#{target_host} - Encryption #{encryption} is deprecated and should not be used.")
report_vuln(
host: target_host,
port: rport,
proto: 'tcp',
name: name,
info: "Module #{fullname} confirmed SSH Encryption #{encryption} is available, but should be deprecated",
refs: refs
)
note = 'Deprecated'
end
table << ['encryption.encryption', encryption, note]
end
table
end
def check_kex(server_data)
table = []
kex_checks = {
'gss-group1-sha1-*' => ['https://datatracker.ietf.org/doc/html/rfc8732#name-deprecated-algorithms'],
'gss-group14-sha1-gss-gex-sha1-*' => ['https://datatracker.ietf.org/doc/html/rfc8732#name-deprecated-algorithms'],
'gss-gex-sha1-*' => ['https://datatracker.ietf.org/doc/html/rfc8732#name-deprecated-algorithms'],
'ecdsa-sha2-nistp521' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#key-exchange'],
'ecdsa-sha2-nistp384' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#key-exchange'],
'ecdsa-sha2-nistp256' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#key-exchange'],
'diffie-hellman-group-exchange-sha1' => ['https://datatracker.ietf.org/doc/html/draft-ietf-curdle-ssh-kex-sha2-20#page-16'],
'diffie-hellman-group1-sha1' => ['https://datatracker.ietf.org/doc/html/draft-ietf-curdle-ssh-kex-sha2-20#page-16'],
'rsa1024-sha1' => ['https://datatracker.ietf.org/doc/html/draft-ietf-curdle-ssh-kex-sha2-20#page-16']
}
server_data[:kex].each do |kex|
note = ''
kex_checks.each do |bad_kex, refs|
if bad_kex.ends_with? '*'
next unless kex.downcase.start_with? bad_kex[0..-2]
else
next unless kex.downcase == bad_kex
end
vprint_good("#{target_host} - Key Exchange (kex) #{kex} is deprecated and should not be used.")
report_vuln(
host: target_host,
port: rport,
proto: 'tcp',
name: name,
info: "Module #{fullname} confirmed SSH Encryption #{kex} is available, but should be deprecated",
refs: refs
)
note = 'Deprecated'
end
table << ['encryption.key_exchange', kex, note]
end
table
end
def check_hmac(server_data)
table = []
hmac_checks = {
'hmac-sha2-512-96' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#message-authentication-code-algorithms'],
'hmac-sha2-256-96' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#message-authentication-code-algorithms'],
'hmac-sha1-96' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#message-authentication-code-algorithms'],
'hmac-ripemd160' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#message-authentication-code-algorithms'],
'hmac-md5' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#message-authentication-code-algorithms'],
'hmac-md5-96' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#message-authentication-code-algorithms'],
'none' => ['https://github.com/net-ssh/net-ssh?tab=readme-ov-file#message-authentication-code-algorithms']
}
server_data[:hmac_server].each do |hmac|
note = ''
hmac_checks.each do |bad_hmac, refs|
next unless hmac.downcase == bad_hmac
vprint_good("#{target_host} - HMAC #{hmac} is deprecated and should not be used.")
report_vuln(
host: target_host,
port: rport,
proto: 'tcp',
name: name,
info: "Module #{fullname} confirmed SSH HMAC #{hmac} is available, but should be deprecated",
refs: refs
)
note = 'Deprecated'
end
table << ['encryption.hmac', hmac, note]
end
table
end
def run_host(target_host)
::Timeout.timeout(timeout) do
transport = Net::SSH::Transport::Session.new(target_host, { port: rport })
server_data = transport.algorithms.instance_variable_get(:@server_data)
host_keys = transport.algorithms.session.instance_variable_get(:@host_keys).instance_variable_get(:@host_keys)
host_keys.each do |host_key|
print_status("#{target_host} - Key Fingerprint: #{host_key.ssh_type} #{Base64.strict_encode64(host_key.to_blob)}")
end
ident = transport.server_version.version
print_status("#{target_host} - SSH server version: #{ident}")
report_service(host: target_host, port: rport, name: 'ssh', proto: 'tcp', info: ident)
return unless datastore['EXTENDED_CHECKS']
table = Rex::Text::Table.new(
'Header' => 'Server Information and Encryption',
'Indent' => 2,
'SortIndex' => 0,
'Columns' => %w[Type Value Note]
)
# if these ever get expanded to have checks, they should be moved to their own function
server_data[:language_server].each do |language|
table << ['encryption.language', language, '']
end
# if these ever get expanded to have checks, they should be moved to their own function
server_data[:compression_server].each do |compression|
table << ['encryption.compression', compression, '']
end
table.rows.concat check_kex(server_data)
table.rows.concat check_host_key(server_data)
table.rows.concat check_hmac(server_data)
table.rows.concat check_encryption(server_data)
table.rows.concat perform_recog(ident)
# XXX check for host key size?
# h00die - not sure how to get that info from the library.
# https://www.tenable.com/plugins/nessus/153954
print_status("#{target_host} - #{table}")
end
rescue EOFError, Rex::ConnectionError => e
vprint_error("#{target_host} - #{e.message}") # This may be a little noisy, but it is consistent
rescue Timeout::Error
vprint_warning("#{target_host} - Timed out after #{timeout} seconds. Skipping.")
end
end