ssh_scan 0.0.38 → 0.0.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +3 -3
- data/README.md +2 -2
- data/bin/ssh_scan +19 -4
- data/config/policies/just_etm_macs.yaml +24 -0
- data/lib/ssh_scan/client.rb +3 -0
- data/lib/ssh_scan/public_key.rb +64 -0
- data/lib/ssh_scan/result.rb +16 -7
- data/lib/ssh_scan/scan_engine.rb +36 -35
- data/lib/ssh_scan/ssh_fp.rb +47 -0
- data/lib/ssh_scan/version.rb +1 -1
- data/lib/string_ext.rb +25 -0
- data/ssh_scan.gemspec +1 -1
- metadata +7 -5
- data/lib/ssh_scan/crypto.rb +0 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 035ffcd050babf1ef147195f6981a84a331e41d6929479a13383692b7148261f
|
4
|
+
data.tar.gz: 5354ffacae2dcf14ecc9a8ebe640baf96cf450ed08ad18dd0fb0bafe818131af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a859e1d12f2c6479f15d8e34a2b71c6d040f6d7913ef7d661209ad902f73b901c2fb79f439656d68e43275b57db41e10a7f9d0aa939404e5978dc2067cde124
|
7
|
+
data.tar.gz: 596a3102e97a68d74ebe0d698eeaef1687229e896a44fd7152241b658d0f7a6b28c5a9dd1b94e31215b52ebf1ad3179b4a0a614f97cfde9b8f4f7b460c9cf714
|
data/CONTRIBUTING.md
CHANGED
@@ -27,9 +27,9 @@ inclusion, but sending a pull request is much more awesome.
|
|
27
27
|
|
28
28
|
If you want your pull requests to be accepted, please follow the following guidelines:
|
29
29
|
|
30
|
-
- [**Add tests!**](
|
30
|
+
- [**Add tests!**](https://rspec.info/) Your patch won't be accepted (or will be delayed) if it doesn't have tests.
|
31
31
|
|
32
|
-
- [**Document any change in behaviour**](
|
32
|
+
- [**Document any change in behaviour**](https://yardoc.org/) Make sure the README and any other
|
33
33
|
relevant documentation are kept up-to-date.
|
34
34
|
|
35
35
|
- [**Create topic branches**](https://github.com/dchelimsky/rspec/wiki/Topic-Branches) Don't ask us to pull from your master branch.
|
@@ -37,7 +37,7 @@ If you want your pull requests to be accepted, please follow the following guide
|
|
37
37
|
- [**One pull request per feature**](https://help.github.com/articles/using-pull-requests) If you want to do more than one thing, send
|
38
38
|
multiple pull requests.
|
39
39
|
|
40
|
-
- [**Send coherent history**](
|
40
|
+
- [**Send coherent history**](https://stackoverflow.com/questions/6934752/git-combining-multiple-commits-before-pushing) Make sure each individual commit in your pull
|
41
41
|
request is meaningful. If you had to make multiple intermediate commits while
|
42
42
|
developing, please squash them before sending them to us.
|
43
43
|
|
data/README.md
CHANGED
@@ -9,7 +9,7 @@ A SSH configuration and policy scanner
|
|
9
9
|
|
10
10
|
## Key Benefits
|
11
11
|
|
12
|
-
- **Minimal
|
12
|
+
- **Minimal Dependencies** - Uses native Ruby and BinData to do its work, no heavy dependencies.
|
13
13
|
- **Not Just a Script** - Implementation is portable for use in another project or for automation of tasks.
|
14
14
|
- **Simple** - Just point `ssh_scan` at an SSH service and get a JSON report of what it supports and its policy status.
|
15
15
|
- **Configurable** - Make your own custom policies that fit your unique policy requirements.
|
@@ -89,7 +89,7 @@ Examples:
|
|
89
89
|
|
90
90
|
## ssh_scan as a service/api?
|
91
91
|
|
92
|
-
This project is
|
92
|
+
This project is solely for ssh_scan engine/command-line usage.
|
93
93
|
|
94
94
|
If you would like to run ssh_scan as a service, please refer to [the ssh_scan_api project](https://github.com/mozilla/ssh_scan_api)
|
95
95
|
|
data/bin/ssh_scan
CHANGED
@@ -20,7 +20,7 @@ options = {
|
|
20
20
|
"verbosity" => nil,
|
21
21
|
"logger" => Logger.new(STDERR),
|
22
22
|
"fingerprint_database" => ENV['HOME']+'/.ssh_scan_fingerprints.yml',
|
23
|
-
"output_type" =>
|
23
|
+
"output_type" => nil
|
24
24
|
}
|
25
25
|
|
26
26
|
# Reorder arguments before parsing
|
@@ -106,7 +106,8 @@ scan") do |file|
|
|
106
106
|
|
107
107
|
opts.on("-o", "--output [FilePath]",
|
108
108
|
"File to write JSON output to") do |file|
|
109
|
-
|
109
|
+
options["output"] = file
|
110
|
+
# $stdout.reopen(file, "w")
|
110
111
|
end
|
111
112
|
|
112
113
|
opts.on("--output-type [json, yaml]",
|
@@ -238,9 +239,23 @@ options["policy_file"] = SSHScan::Policy.from_file(options["policy"])
|
|
238
239
|
scan_engine = SSHScan::ScanEngine.new()
|
239
240
|
results = scan_engine.scan(options)
|
240
241
|
|
241
|
-
if options["output_type"] == "yaml"
|
242
|
+
if options["output_type"] == "yaml" && (options["output"].nil? || options["output"].empty?)
|
242
243
|
puts YAML.dump(results)
|
243
|
-
elsif options["output_type"] == "json"
|
244
|
+
elsif options["output_type"] == "json" && (options["output"].nil? || options["output"].empty?)
|
245
|
+
puts JSON.pretty_generate(results)
|
246
|
+
elsif (options["output_type"].nil? || options["output_type"].empty?) && (!options["output"].nil? && options["output"].split(".").last.downcase == "yml")
|
247
|
+
open(options["output"], 'w') do |f|
|
248
|
+
f.puts YAML.dump(results)
|
249
|
+
end
|
250
|
+
elsif (options["output_type"].nil? || options["output_type"].empty?) && (!options["output"].nil? && options["output"].split(".").last.downcase == "json")
|
251
|
+
open(options["output"], 'w') do |f|
|
252
|
+
f.puts JSON.pretty_generate(results)
|
253
|
+
end
|
254
|
+
elsif (options["output_type"].nil? || options["output_type"].empty?) && options["output"]
|
255
|
+
open(options["output"], 'w') do |f|
|
256
|
+
f.puts JSON.pretty_generate(results)
|
257
|
+
end
|
258
|
+
else
|
244
259
|
puts JSON.pretty_generate(results)
|
245
260
|
end
|
246
261
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
name: Mozilla Modern - with just ETM macs
|
3
|
+
ssh_version: 2.0
|
4
|
+
auth_methods:
|
5
|
+
- publickey
|
6
|
+
kex:
|
7
|
+
- curve25519-sha256@libssh.org
|
8
|
+
- ecdh-sha2-nistp521
|
9
|
+
- ecdh-sha2-nistp384
|
10
|
+
- ecdh-sha2-nistp256
|
11
|
+
- diffie-hellman-group-exchange-sha256
|
12
|
+
encryption:
|
13
|
+
- chacha20-poly1305@openssh.com
|
14
|
+
- aes256-gcm@openssh.com
|
15
|
+
- aes128-gcm@openssh.com
|
16
|
+
- aes256-ctr
|
17
|
+
- aes192-ctr
|
18
|
+
- aes128-ctr
|
19
|
+
macs:
|
20
|
+
- hmac-sha2-512-etm@openssh.com
|
21
|
+
- hmac-sha2-256-etm@openssh.com
|
22
|
+
- umac-128-etm@openssh.com
|
23
|
+
references:
|
24
|
+
- https://example.com/custom_policy
|
data/lib/ssh_scan/client.rb
CHANGED
@@ -70,6 +70,9 @@ module SSHScan
|
|
70
70
|
rescue Errno::EHOSTUNREACH => e
|
71
71
|
@error = SSHScan::Error::ConnectionRefused.new(e.message)
|
72
72
|
@sock = nil
|
73
|
+
rescue Errno::ENOPROTOOPT => e
|
74
|
+
@error = SSHScan::Error::ConnectionRefused.new(e.message)
|
75
|
+
@sock = nil
|
73
76
|
else
|
74
77
|
if @raw_server_banner.nil?
|
75
78
|
@error = SSHScan::Error::NoBanner.new(
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'sshkey'
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
module SSHScan
|
6
|
+
# All cryptography related methods.
|
7
|
+
module Crypto
|
8
|
+
# House methods helpful in analysing SSH public keys.
|
9
|
+
class PublicKey
|
10
|
+
def initialize(key_string)
|
11
|
+
@key_string = key_string
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid?
|
15
|
+
SSHKey.valid_ssh_public_key?(@key_string)
|
16
|
+
end
|
17
|
+
|
18
|
+
def type
|
19
|
+
if @key_string.start_with?("ssh-rsa")
|
20
|
+
return "rsa"
|
21
|
+
elsif @key_string.start_with?("ssh-dss")
|
22
|
+
return "dsa"
|
23
|
+
elsif @key_string.start_with?("ecdsa-sha2-nistp256")
|
24
|
+
return "ecdsa-sha2-nistp256"
|
25
|
+
elsif @key_string.start_with?("ssh-ed25519")
|
26
|
+
return "ed25519"
|
27
|
+
else
|
28
|
+
return "unknown"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def length
|
33
|
+
SSHKey.ssh_public_key_bits(@key_string)
|
34
|
+
end
|
35
|
+
|
36
|
+
def fingerprint_md5
|
37
|
+
SSHKey.fingerprint(@key_string)
|
38
|
+
end
|
39
|
+
|
40
|
+
def fingerprint_sha1
|
41
|
+
SSHKey.sha1_fingerprint(@key_string)
|
42
|
+
end
|
43
|
+
|
44
|
+
def fingerprint_sha256
|
45
|
+
# We're translating this to hex because the SSHKEY default isn't as useful for comparing with SSHFP records
|
46
|
+
Base64.decode64(SSHKey.sha256_fingerprint(@key_string)).hexify(:delim => ":")
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_hash
|
50
|
+
{
|
51
|
+
self.type => {
|
52
|
+
"raw" => @key_string,
|
53
|
+
"length" => self.length,
|
54
|
+
"fingerprints" => {
|
55
|
+
"md5" => self.fingerprint_md5,
|
56
|
+
"sha1" => self.fingerprint_sha1,
|
57
|
+
"sha256" => self.fingerprint_sha256
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/ssh_scan/result.rb
CHANGED
@@ -8,7 +8,7 @@ module SSHScan
|
|
8
8
|
class Result
|
9
9
|
def initialize()
|
10
10
|
@version = SSHScan::VERSION
|
11
|
-
@
|
11
|
+
@keys = nil
|
12
12
|
@duplicate_host_key_ips = Set.new()
|
13
13
|
@compliance = {}
|
14
14
|
end
|
@@ -157,12 +157,20 @@ module SSHScan
|
|
157
157
|
@auth_methods = auth_methods
|
158
158
|
end
|
159
159
|
|
160
|
-
def
|
161
|
-
@
|
160
|
+
def keys
|
161
|
+
@keys || {}
|
162
162
|
end
|
163
163
|
|
164
|
-
def
|
165
|
-
@
|
164
|
+
def keys=(keys)
|
165
|
+
@keys = keys
|
166
|
+
end
|
167
|
+
|
168
|
+
def dns_keys
|
169
|
+
@dns_keys
|
170
|
+
end
|
171
|
+
|
172
|
+
def dns_keys=(dns_keys)
|
173
|
+
@dns_keys = dns_keys
|
166
174
|
end
|
167
175
|
|
168
176
|
def duplicate_host_key_ips=(duplicate_host_key_ips)
|
@@ -249,8 +257,9 @@ module SSHScan
|
|
249
257
|
"languages_client_to_server" => self.languages_client_to_server,
|
250
258
|
"languages_server_to_client" => self.languages_server_to_client,
|
251
259
|
"auth_methods" => self.auth_methods,
|
252
|
-
"
|
253
|
-
"
|
260
|
+
"keys" => self.keys,
|
261
|
+
"dns_keys" => self.dns_keys,
|
262
|
+
"duplicate_host_key_ips" => self.duplicate_host_key_ips.uniq,
|
254
263
|
"compliance" => @compliance,
|
255
264
|
"start_time" => self.start_time,
|
256
265
|
"end_time" => self.end_time,
|
data/lib/ssh_scan/scan_engine.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'ssh_scan/client'
|
3
|
-
require 'ssh_scan/
|
3
|
+
require 'ssh_scan/public_key'
|
4
4
|
require 'ssh_scan/fingerprint_database'
|
5
5
|
require 'ssh_scan/subprocess'
|
6
|
+
require 'ssh_scan/ssh_fp'
|
6
7
|
require 'net/ssh'
|
7
8
|
require 'logger'
|
8
9
|
require 'open3'
|
@@ -119,11 +120,11 @@ module SSHScan
|
|
119
120
|
end
|
120
121
|
|
121
122
|
# Figure out what rsa or dsa fingerprints exist
|
122
|
-
|
123
|
+
keys = {}
|
123
124
|
|
124
125
|
output = ""
|
125
126
|
|
126
|
-
cmd = ['ssh-keyscan', '-t', 'rsa,dsa', '-p', port.to_s, target].join(" ")
|
127
|
+
cmd = ['ssh-keyscan', '-t', 'rsa,dsa,ecdsa,ed25519', '-p', port.to_s, target].join(" ")
|
127
128
|
|
128
129
|
Utils::Subprocess.new(cmd) do |stdout, stderr, thread|
|
129
130
|
if stdout
|
@@ -136,31 +137,27 @@ module SSHScan
|
|
136
137
|
|
137
138
|
for i in 0..host_keys_len
|
138
139
|
if host_keys[i].eql? "ssh-dss"
|
139
|
-
|
140
|
-
|
141
|
-
"dsa" => {
|
142
|
-
"known_bad" => pkey.bad_key?.to_s,
|
143
|
-
"md5" => pkey.fingerprint_md5,
|
144
|
-
"sha1" => pkey.fingerprint_sha1,
|
145
|
-
"sha256" => pkey.fingerprint_sha256,
|
146
|
-
}
|
147
|
-
})
|
140
|
+
key = SSHScan::Crypto::PublicKey.new([host_keys[i], host_keys[i + 1]].join(" "))
|
141
|
+
keys.merge!(key.to_hash)
|
148
142
|
end
|
149
143
|
|
150
144
|
if host_keys[i].eql? "ssh-rsa"
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
145
|
+
key = SSHScan::Crypto::PublicKey.new([host_keys[i], host_keys[i + 1]].join(" "))
|
146
|
+
keys.merge!(key.to_hash)
|
147
|
+
end
|
148
|
+
|
149
|
+
if host_keys[i].eql? "ecdsa-sha2-nistp256"
|
150
|
+
key = SSHScan::Crypto::PublicKey.new([host_keys[i], host_keys[i + 1]].join(" "))
|
151
|
+
keys.merge!(key.to_hash)
|
152
|
+
end
|
153
|
+
|
154
|
+
if host_keys[i].eql? "ssh-ed25519"
|
155
|
+
key = SSHScan::Crypto::PublicKey.new([host_keys[i], host_keys[i + 1]].join(" "))
|
156
|
+
keys.merge!(key.to_hash)
|
160
157
|
end
|
161
158
|
end
|
162
159
|
|
163
|
-
result.
|
160
|
+
result.keys = keys
|
164
161
|
result.set_end_time
|
165
162
|
|
166
163
|
return result
|
@@ -200,12 +197,10 @@ module SSHScan
|
|
200
197
|
results.each do |result|
|
201
198
|
fingerprint_db.clear_fingerprints(result.ip)
|
202
199
|
|
203
|
-
if result.
|
204
|
-
result.
|
205
|
-
host_key_algo.each do |fingerprint|
|
206
|
-
|
207
|
-
next if key == "known_bad"
|
208
|
-
fingerprint_db.add_fingerprint(value, result.ip)
|
200
|
+
if result.keys
|
201
|
+
result.keys.values.each do |host_key_algo|
|
202
|
+
host_key_algo['fingerprints'].values.each do |fingerprint|
|
203
|
+
fingerprint_db.add_fingerprint(fingerprint, result.ip)
|
209
204
|
end
|
210
205
|
end
|
211
206
|
end
|
@@ -213,20 +208,26 @@ module SSHScan
|
|
213
208
|
|
214
209
|
# Decorate all the results with duplicate keys
|
215
210
|
results.each do |result|
|
216
|
-
if result.
|
211
|
+
if result.keys
|
217
212
|
ip = result.ip
|
218
213
|
result.duplicate_host_key_ips = []
|
219
|
-
result.
|
220
|
-
host_key_algo.each do |fingerprint|
|
221
|
-
|
222
|
-
next if key == "known_bad"
|
223
|
-
fingerprint_db.find_fingerprints(value).each do |other_ip|
|
214
|
+
result.keys.values.each do |host_key_algo|
|
215
|
+
host_key_algo["fingerprints"].values.each do |fingerprint|
|
216
|
+
fingerprint_db.find_fingerprints(fingerprint).each do |other_ip|
|
224
217
|
next if ip == other_ip
|
225
218
|
result.duplicate_host_key_ips << other_ip
|
226
219
|
end
|
227
220
|
end
|
228
221
|
end
|
229
|
-
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Decorate all the results with SSHFP records
|
226
|
+
sshfp = SSHScan::SshFp.new()
|
227
|
+
results.each do |result|
|
228
|
+
if !result.hostname.empty?
|
229
|
+
dns_keys = sshfp.query(result.hostname)
|
230
|
+
result.dns_keys = dns_keys
|
230
231
|
end
|
231
232
|
end
|
232
233
|
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
|
3
|
+
module SSHScan
|
4
|
+
class SshFp
|
5
|
+
|
6
|
+
ALGO_MAP = {
|
7
|
+
0 => "reserved", # Reference: https://tools.ietf.org/html/rfc4255#section-2.4
|
8
|
+
1 => "rsa", # Reference: https://tools.ietf.org/html/rfc4255#section-2.4
|
9
|
+
2 => "dss", # Reference: https://tools.ietf.org/html/rfc4255#section-2.4
|
10
|
+
3 => "ecdsa", # Reference: https://tools.ietf.org/html/rfc6594#section-5.3.1
|
11
|
+
4 => "ed25519" # Reference: https://tools.ietf.org/html/rfc7479
|
12
|
+
}
|
13
|
+
|
14
|
+
|
15
|
+
FPTYPE_MAP = {
|
16
|
+
0 => "reserved", # Reference: https://tools.ietf.org/html/rfc4255#section-2.4
|
17
|
+
1 => "sha1", # Reference: https://tools.ietf.org/html/rfc4255#section-2.4
|
18
|
+
2 => "sha256" # Reference: https://tools.ietf.org/html/rfc6594#section-5.1.2
|
19
|
+
}
|
20
|
+
|
21
|
+
|
22
|
+
def query(fqdn)
|
23
|
+
sshfp_records = []
|
24
|
+
|
25
|
+
# Reference: https://stackoverflow.com/questions/28867626/how-to-use-resolvdnsresourcegeneric
|
26
|
+
# Note: this includes some fixes too, I'll post a direct link back to the SO article.
|
27
|
+
Resolv::DNS.open do |dns|
|
28
|
+
all_records = dns.getresources(fqdn, Resolv::DNS::Resource::IN::ANY ) rescue nil
|
29
|
+
all_records.each do |rr|
|
30
|
+
if rr.is_a? Resolv::DNS::Resource::Generic then
|
31
|
+
classname = rr.class.name.split('::').last
|
32
|
+
if classname == "Type44_Class1"
|
33
|
+
data = rr.data.bytes
|
34
|
+
algo = data[0].to_s
|
35
|
+
fptype = data[1].to_s
|
36
|
+
fp = data[2..-1]
|
37
|
+
hex = fp.map{|b| b.to_s(16).rjust(2,'0') }.join(':')
|
38
|
+
sshfp_records << {"fptype" => FPTYPE_MAP[fptype.to_i], "algo" => ALGO_MAP[algo.to_i], "hex" => hex}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
return sshfp_records.sort_by { |k| k["hex"] }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/ssh_scan/version.rb
CHANGED
data/lib/string_ext.rb
CHANGED
@@ -69,6 +69,31 @@ class String
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
+
# Stolen from: https://github.com/emonti/rbkb/blob/master/lib/rbkb/extends/string.rb
|
73
|
+
def hexify(opts={})
|
74
|
+
delim = opts[:delim]
|
75
|
+
pre = (opts[:prefix] || "")
|
76
|
+
suf = (opts[:suffix] || "")
|
77
|
+
|
78
|
+
if (rx=opts[:rx]) and not rx.kind_of? Regexp
|
79
|
+
raise "rx must be a regular expression for a character class"
|
80
|
+
end
|
81
|
+
|
82
|
+
hx = [("0".."9").to_a, ("a".."f").to_a].flatten
|
83
|
+
|
84
|
+
out=Array.new
|
85
|
+
|
86
|
+
self.each_byte do |c|
|
87
|
+
hc = if (rx and not rx.match c.chr)
|
88
|
+
c.chr
|
89
|
+
else
|
90
|
+
pre + (hx[(c >> 4)] + hx[(c & 0xf )]) + suf
|
91
|
+
end
|
92
|
+
out << (hc)
|
93
|
+
end
|
94
|
+
out.join(delim)
|
95
|
+
end
|
96
|
+
|
72
97
|
def fqdn?
|
73
98
|
begin
|
74
99
|
resolve_fqdn
|
data/ssh_scan.gemspec
CHANGED
@@ -32,7 +32,7 @@ Gem::Specification.new do |s|
|
|
32
32
|
|
33
33
|
s.add_dependency('bindata', '2.4.3')
|
34
34
|
s.add_dependency('netaddr', '1.5.1')
|
35
|
-
s.add_dependency('net-ssh', '5.0
|
35
|
+
s.add_dependency('net-ssh', '5.2.0')
|
36
36
|
s.add_dependency('sshkey')
|
37
37
|
s.add_development_dependency('pry', '0.11.3')
|
38
38
|
s.add_development_dependency('rspec', '3.7.0')
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ssh_scan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.43
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Claudius
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date:
|
15
|
+
date: 2020-05-27 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: bindata
|
@@ -48,14 +48,14 @@ dependencies:
|
|
48
48
|
requirements:
|
49
49
|
- - '='
|
50
50
|
- !ruby/object:Gem::Version
|
51
|
-
version: 5.0
|
51
|
+
version: 5.2.0
|
52
52
|
type: :runtime
|
53
53
|
prerelease: false
|
54
54
|
version_requirements: !ruby/object:Gem::Requirement
|
55
55
|
requirements:
|
56
56
|
- - '='
|
57
57
|
- !ruby/object:Gem::Version
|
58
|
-
version: 5.0
|
58
|
+
version: 5.2.0
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
60
|
name: sshkey
|
61
61
|
requirement: !ruby/object:Gem::Requirement
|
@@ -155,6 +155,7 @@ files:
|
|
155
155
|
- README.md
|
156
156
|
- Rakefile
|
157
157
|
- bin/ssh_scan
|
158
|
+
- config/policies/just_etm_macs.yaml
|
158
159
|
- config/policies/mozilla_intermediate.yml
|
159
160
|
- config/policies/mozilla_modern.yml
|
160
161
|
- data/README
|
@@ -163,7 +164,6 @@ files:
|
|
163
164
|
- lib/ssh_scan/banner.rb
|
164
165
|
- lib/ssh_scan/client.rb
|
165
166
|
- lib/ssh_scan/constants.rb
|
166
|
-
- lib/ssh_scan/crypto.rb
|
167
167
|
- lib/ssh_scan/error.rb
|
168
168
|
- lib/ssh_scan/error/closed_connection.rb
|
169
169
|
- lib/ssh_scan/error/connect_timeout.rb
|
@@ -188,8 +188,10 @@ files:
|
|
188
188
|
- lib/ssh_scan/policy.rb
|
189
189
|
- lib/ssh_scan/policy_manager.rb
|
190
190
|
- lib/ssh_scan/protocol.rb
|
191
|
+
- lib/ssh_scan/public_key.rb
|
191
192
|
- lib/ssh_scan/result.rb
|
192
193
|
- lib/ssh_scan/scan_engine.rb
|
194
|
+
- lib/ssh_scan/ssh_fp.rb
|
193
195
|
- lib/ssh_scan/ssh_lib.rb
|
194
196
|
- lib/ssh_scan/ssh_lib/ciscossh.rb
|
195
197
|
- lib/ssh_scan/ssh_lib/cryptlib.rb
|
data/lib/ssh_scan/crypto.rb
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
require 'openssl'
|
2
|
-
require 'sshkey'
|
3
|
-
require 'base64'
|
4
|
-
|
5
|
-
module SSHScan
|
6
|
-
# All cryptography related methods.
|
7
|
-
module Crypto
|
8
|
-
# House methods helpful in analysing SSH public keys.
|
9
|
-
class PublicKey
|
10
|
-
def initialize(key)
|
11
|
-
@key = key
|
12
|
-
end
|
13
|
-
|
14
|
-
# Is the current key known to be in our known bad key list
|
15
|
-
# @return [Boolean] true if this {SSHScan::Crypto::PublicKey}
|
16
|
-
# instance's key is also in {SSHScan::Crypto}'s
|
17
|
-
# bad_public_keys, otherwise false
|
18
|
-
def bad_key?
|
19
|
-
SSHScan::Crypto.bad_public_keys.each do |other_key|
|
20
|
-
if self.fingerprint_sha256 == other_key.fingerprint_sha256
|
21
|
-
return true
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
return false
|
26
|
-
end
|
27
|
-
|
28
|
-
# Generate MD5 fingerprint for this {SSHScan::Crypto::PublicKey} instance.
|
29
|
-
# @return [String] formatted MD5 fingerprint
|
30
|
-
def fingerprint_md5
|
31
|
-
OpenSSL::Digest::MD5.hexdigest(::Base64.decode64(@key)).scan(/../).join(':')
|
32
|
-
end
|
33
|
-
|
34
|
-
# Generate SHA1 fingerprint for this {SSHScan::Crypto::PublicKey} instance.
|
35
|
-
# @return [String] formatted SHA1 fingerprint
|
36
|
-
def fingerprint_sha1
|
37
|
-
OpenSSL::Digest::SHA1.hexdigest(::Base64.decode64(@key)).scan(/../).join(':')
|
38
|
-
end
|
39
|
-
|
40
|
-
# Generate SHA256 fingerprint for this {SSHScan::Crypto::PublicKey} instance.
|
41
|
-
# @return [String] formatted SHA256 fingerprint
|
42
|
-
def fingerprint_sha256
|
43
|
-
OpenSSL::Digest::SHA256.hexdigest(::Base64.decode64(@key)).scan(/../).join(':')
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.bad_public_keys
|
48
|
-
bad_keys = []
|
49
|
-
|
50
|
-
Dir.glob("data/ssh-badkeys/host/*.key").each do |file_path|
|
51
|
-
file = File.read(File.expand_path(file_path))
|
52
|
-
key = SSHKey.new(file)
|
53
|
-
bad_keys << SSHScan::Crypto::PublicKey.new(key.ssh_public_key.split[1])
|
54
|
-
end
|
55
|
-
|
56
|
-
return bad_keys
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
end
|