##
# 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'],
'[email protected]' => ['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
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation