Skip to content
This repository was archived by the owner on Jan 24, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all 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
60 changes: 0 additions & 60 deletions lib/ssh_scan/crypto.rb

This file was deleted.

59 changes: 59 additions & 0 deletions lib/ssh_scan/public_key.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
require 'openssl'
require 'sshkey'
require 'base64'

module SSHScan
# All cryptography related methods.
module Crypto
# House methods helpful in analysing SSH public keys.
class PublicKey
def initialize(key_string)
@key_string = key_string
end

def valid?
SSHKey.valid_ssh_public_key?(@key_string)
end

def type
if @key_string.start_with?("ssh-rsa")
return "rsa"
elsif @key_string.start_with?("ssh-dss")
return "dsa"
else
return "unknown"
end
end

def length
SSHKey.ssh_public_key_bits(@key_string)
end

def fingerprint_md5
SSHKey.fingerprint(@key_string)
end

def fingerprint_sha1
SSHKey.sha1_fingerprint(@key_string)
end

def fingerprint_sha256
SSHKey.sha256_fingerprint(@key_string)
end

def to_hash
{
self.type => {
"raw" => @key_string,
"length" => self.length,
"fingerprints" => {
"md5" => self.fingerprint_md5,
"sha1" => self.fingerprint_sha1,
"sha256" => self.fingerprint_sha256
}
}
}
end
end
end
end
14 changes: 7 additions & 7 deletions lib/ssh_scan/result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module SSHScan
class Result
def initialize()
@version = SSHScan::VERSION
@fingerprints = nil
@keys = nil
@duplicate_host_key_ips = Set.new()
@compliance = {}
end
Expand Down Expand Up @@ -157,12 +157,12 @@ def auth_methods=(auth_methods)
@auth_methods = auth_methods
end

def fingerprints=(fingerprints)
@fingerprints = fingerprints
def keys=(keys)
@keys = keys
end

def fingerprints
@fingerprints
def keys
@keys
end

def duplicate_host_key_ips=(duplicate_host_key_ips)
Expand Down Expand Up @@ -249,8 +249,8 @@ def to_hash
"languages_client_to_server" => self.languages_client_to_server,
"languages_server_to_client" => self.languages_server_to_client,
"auth_methods" => self.auth_methods,
"fingerprints" => self.fingerprints,
"duplicate_host_key_ips" => self.duplicate_host_key_ips,
"keys" => self.keys,
"duplicate_host_key_ips" => self.duplicate_host_key_ips.uniq,
"compliance" => @compliance,
"start_time" => self.start_time,
"end_time" => self.end_time,
Expand Down
49 changes: 15 additions & 34 deletions lib/ssh_scan/scan_engine.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require 'socket'
require 'ssh_scan/client'
require 'ssh_scan/crypto'
require 'ssh_scan/public_key'
require 'ssh_scan/fingerprint_database'
require 'ssh_scan/subprocess'
require 'net/ssh'
Expand Down Expand Up @@ -119,7 +119,7 @@ def scan_target(socket, opts)
end

# Figure out what rsa or dsa fingerprints exist
fingerprints = {}
keys = {}

output = ""

Expand All @@ -136,31 +136,17 @@ def scan_target(socket, opts)

for i in 0..host_keys_len
if host_keys[i].eql? "ssh-dss"
pkey = SSHScan::Crypto::PublicKey.new(host_keys[i + 1])
fingerprints.merge!({
"dsa" => {
"known_bad" => pkey.bad_key?.to_s,
"md5" => pkey.fingerprint_md5,
"sha1" => pkey.fingerprint_sha1,
"sha256" => pkey.fingerprint_sha256,
}
})
key = SSHScan::Crypto::PublicKey.new([host_keys[i], host_keys[i + 1]].join(" "))
keys.merge!(key.to_hash)
end

if host_keys[i].eql? "ssh-rsa"
pkey = SSHScan::Crypto::PublicKey.new(host_keys[i + 1])
fingerprints.merge!({
"rsa" => {
"known_bad" => pkey.bad_key?.to_s,
"md5" => pkey.fingerprint_md5,
"sha1" => pkey.fingerprint_sha1,
"sha256" => pkey.fingerprint_sha256,
}
})
key = SSHScan::Crypto::PublicKey.new([host_keys[i], host_keys[i + 1]].join(" "))
keys.merge!(key.to_hash)
end
end

result.fingerprints = fingerprints
result.keys = keys
result.set_end_time

return result
Expand Down Expand Up @@ -200,33 +186,28 @@ def scan(opts)
results.each do |result|
fingerprint_db.clear_fingerprints(result.ip)

if result.fingerprints
result.fingerprints.values.each do |host_key_algo|
host_key_algo.each do |fingerprint|
key, value = fingerprint
next if key == "known_bad"
fingerprint_db.add_fingerprint(value, result.ip)
if result.keys
result.keys.values.each do |host_key_algo|
host_key_algo['fingerprints'].values.each do |fingerprint|
fingerprint_db.add_fingerprint(fingerprint, result.ip)
end
end
end
end

# Decorate all the results with duplicate keys
results.each do |result|
if result.fingerprints
if result.keys
ip = result.ip
result.duplicate_host_key_ips = []
result.fingerprints.values.each do |host_key_algo|
host_key_algo.each do |fingerprint|
key, value = fingerprint
next if key == "known_bad"
fingerprint_db.find_fingerprints(value).each do |other_ip|
result.keys.values.each do |host_key_algo|
host_key_algo["fingerprints"].values.each do |fingerprint|
fingerprint_db.find_fingerprints(fingerprint).each do |other_ip|
next if ip == other_ip
result.duplicate_host_key_ips << other_ip
end
end
end
result.duplicate_host_key_ips
end
end

Expand Down
78 changes: 78 additions & 0 deletions spec/public_key_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
require 'spec_helper'
require 'rspec'
require 'ssh_scan/public_key'

describe SSHScan::Crypto::PublicKey do
context "when parsing an RSA key string" do
it "should parse it and have the right values for each attribute" do
key_string = "ssh-rsa " +
"AAAAB3NzaC1yc2EAAAADAQABAAABAQCl/BNLUxR49+3AKqhf6sWKr" +
"h8XXzqXV00bEPFtcJFWxyRqC5pPWo9zRRiS2jitIcqljIQVohEEZH" +
"t48vZaA1hniVfe/FmrFzuCOuQOIP2fuRgLSNHu+lWVScsHoX/MuYX" +
"EIxj6aW7UpFn4lD01mvPtazXFO/tJ+LRs49YBP7UvL1smIS2xoyuH" +
"7kZDN17QG08YwbIB2fApMl8rXH+2Rpj5hlv+7rcZ1dqCGtmXmvsv8" +
"fKGYd7BxRy0s/d7EY4e/DeDxA1qTNV9BrBTNn6jAKIedTE5s4GNRb" +
"N/Q20mP2qmw70PiTGROw6xp9SBFA7N9hjjOT7iutK/pa7y1joXKjeJ"
key = SSHScan::Crypto::PublicKey.new(key_string)
expect(key).to be_kind_of SSHScan::Crypto::PublicKey
expect(key.valid?).to be true
expect(key.type).to eq("rsa")
expect(key.length).to be 2048
expect(key.fingerprint_md5).to eq("fc:c5:5b:0d:f0:c6:fd:fe:80:18:62:2c:05:38:20:8a")
expect(key.fingerprint_sha1).to eq("e1:3c:71:49:80:37:87:32:b5:0c:e3:86:41:ef:2e:2a:2f:14:e3:58")
expect(key.fingerprint_sha256).to eq("aH0wN2cs6x5Ktf9PvIzoQeFVqDBC4I484wq6vNv9XFA=")
expect(key.to_hash).to eq(
{
"rsa" => {
"fingerprints" => {
"md5"=>"fc:c5:5b:0d:f0:c6:fd:fe:80:18:62:2c:05:38:20:8a",
"sha1"=>"e1:3c:71:49:80:37:87:32:b5:0c:e3:86:41:ef:2e:2a:2f:14:e3:58",
"sha256"=>"aH0wN2cs6x5Ktf9PvIzoQeFVqDBC4I484wq6vNv9XFA="
},
"length" => 2048,
"raw" => "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl/BNLUxR49+3AKqhf6sWKrh8XXzqXV00bEPFtcJFWxyRqC5pPWo9zRRiS2jitIcqljIQVohEEZHt48vZaA1hniVfe/FmrFzuCOuQOIP2fuRgLSNHu+lWVScsHoX/MuYXEIxj6aW7UpFn4lD01mvPtazXFO/tJ+LRs49YBP7UvL1smIS2xoyuH7kZDN17QG08YwbIB2fApMl8rXH+2Rpj5hlv+7rcZ1dqCGtmXmvsv8fKGYd7BxRy0s/d7EY4e/DeDxA1qTNV9BrBTNn6jAKIedTE5s4GNRbN/Q20mP2qmw70PiTGROw6xp9SBFA7N9hjjOT7iutK/pa7y1joXKjeJ",
}
}
)
end
end

context "when parsing an DSA key string" do
it "should parse it and have the right values for each attribute" do
key_string = "ssh-dss " +
"AAAAB3NzaC1kc3MAAACBAOXOC6kuB7xDMgHS79KFQITNeAT9tMKd2oK1" +
"c6bQEHRgTSMP3sWZ1cntWVFKl5u6MEuEBBT9PZKWsy7vRE525Wwt+NbR" +
"IBso3vYFF1MtxZKpAsF+gbGI7y+aZcIceXrHkkY2bz3oGb9I9MZ2DSu2" +
"9crW11YHCmuOJ2FJiDcx7dV9AAAAFQC+Ws9e0KJaAsN8cj75DbTQumrd" +
"JQAAAIBjn5EA5JvQg7xu8TRcNmZWhuyBLoOZczU6nk2h4i+x4pbpVMVr" +
"Ch5Lr8wsH60w7IW4yKg6JvPlzmQW0ZRZAwnU9sC3YO64H1RFQg8tnmRr" +
"w0I9oi6wKPEe5rLgbdr9jYHePs9tiV+ZFfUKmXh0s7srr/dwmX/gHCPI" +
"whLEVa+dLQAAAIEAn/+dSyf6KXdfKNyx9MYc1l2/2YUhVuxClF26PNQX" +
"0CZhcSoDyUXU/eAqaS7S6EYqtM/8FK1OZY1tzM5Nm4GWY2LLF22Q2YkK" +
"ItkhfS3GaD5JeuTQ+HK0F+wQjmpqt2pUulVQXQAjvE1qoRFQ4/yeVrvh" +
"VqCzFICnariQP7tMYEo="
key = SSHScan::Crypto::PublicKey.new(key_string)
expect(key).to be_kind_of SSHScan::Crypto::PublicKey
expect(key.valid?).to be true
expect(key.type).to eq("dsa")
expect(key.length).to be 1024
expect(key.fingerprint_md5).to eq("6b:5f:8d:57:be:2e:55:7f:e3:d7:15:d1:66:17:d8:8c")
expect(key.fingerprint_sha1).to eq("49:84:7f:d7:9d:84:2a:20:61:72:10:3f:2c:b1:16:9b:12:5b:e7:07")
expect(key.fingerprint_sha256).to eq("sWZzgrGxzs/aMmcU2w6FyET/Iihd6HL1qNyDZnO0NDw=")
expect(key.to_hash).to eq(
{
"dsa" => {
"fingerprints" => {
"md5"=>"6b:5f:8d:57:be:2e:55:7f:e3:d7:15:d1:66:17:d8:8c",
"sha1"=>"49:84:7f:d7:9d:84:2a:20:61:72:10:3f:2c:b1:16:9b:12:5b:e7:07",
"sha256"=>"sWZzgrGxzs/aMmcU2w6FyET/Iihd6HL1qNyDZnO0NDw="
},
"length" => 1024,
"raw" => "ssh-dss AAAAB3NzaC1kc3MAAACBAOXOC6kuB7xDMgHS79KFQITNeAT9tMKd2oK1c6bQEHRgTSMP3sWZ1cntWVFKl5u6MEuEBBT9PZKWsy7vRE525Wwt+NbRIBso3vYFF1MtxZKpAsF+gbGI7y+aZcIceXrHkkY2bz3oGb9I9MZ2DSu29crW11YHCmuOJ2FJiDcx7dV9AAAAFQC+Ws9e0KJaAsN8cj75DbTQumrdJQAAAIBjn5EA5JvQg7xu8TRcNmZWhuyBLoOZczU6nk2h4i+x4pbpVMVrCh5Lr8wsH60w7IW4yKg6JvPlzmQW0ZRZAwnU9sC3YO64H1RFQg8tnmRrw0I9oi6wKPEe5rLgbdr9jYHePs9tiV+ZFfUKmXh0s7srr/dwmX/gHCPIwhLEVa+dLQAAAIEAn/+dSyf6KXdfKNyx9MYc1l2/2YUhVuxClF26PNQX0CZhcSoDyUXU/eAqaS7S6EYqtM/8FK1OZY1tzM5Nm4GWY2LLF22Q2YkKItkhfS3GaD5JeuTQ+HK0F+wQjmpqt2pUulVQXQAjvE1qoRFQ4/yeVrvhVqCzFICnariQP7tMYEo=",
}
}
)
end
end

end
2 changes: 2 additions & 0 deletions spec/ssh_scan/result_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'spec_helper'
require 'rspec'
require 'ssh_scan/version'
require 'ssh_scan/result'

describe SSHScan::Result do
Expand Down Expand Up @@ -32,6 +33,7 @@
expect(result.start_time).to be_nil
expect(result.end_time).to be_nil
expect(result.scan_duration).to be_nil
expect(result.keys).to be_nil
end

context "when setting IP" do
Expand Down