ssh_scan 0.0.16 → 0.0.17.pre

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.
@@ -2,7 +2,8 @@ require 'yaml'
2
2
 
3
3
  module SSHScan
4
4
  class Policy
5
- attr_reader :name, :kex, :macs, :encryption, :compression, :references, :auth_methods, :ssh_version
5
+ attr_reader :name, :kex, :macs, :encryption, :compression,
6
+ :references, :auth_methods, :ssh_version
6
7
 
7
8
  def initialize(opts = {})
8
9
  @name = opts['name'] || []
@@ -6,7 +6,9 @@ module SSHScan
6
6
  end
7
7
 
8
8
  def out_of_policy_encryption
9
- target_encryption = @result[:encryption_algorithms_client_to_server] | @result[:encryption_algorithms_server_to_client]
9
+ target_encryption =
10
+ @result[:encryption_algorithms_client_to_server] |
11
+ @result[:encryption_algorithms_server_to_client]
10
12
  outliers = []
11
13
  target_encryption.each do |target_enc|
12
14
  outliers << target_enc unless @policy.encryption.include?(target_enc)
@@ -15,7 +17,9 @@ module SSHScan
15
17
  end
16
18
 
17
19
  def missing_policy_encryption
18
- target_encryption = @result[:encryption_algorithms_client_to_server] | @result[:encryption_algorithms_server_to_client]
20
+ target_encryption =
21
+ @result[:encryption_algorithms_client_to_server] |
22
+ @result[:encryption_algorithms_server_to_client]
19
23
  outliers = []
20
24
  @policy.encryption.each do |encryption|
21
25
  if target_encryption.include?(encryption) == false
@@ -26,7 +30,9 @@ module SSHScan
26
30
  end
27
31
 
28
32
  def out_of_policy_macs
29
- target_macs = @result[:mac_algorithms_server_to_client] | @result[:mac_algorithms_client_to_server]
33
+ target_macs =
34
+ @result[:mac_algorithms_server_to_client] |
35
+ @result[:mac_algorithms_client_to_server]
30
36
  outliers = []
31
37
  target_macs.each do |target_mac|
32
38
  outliers << target_mac unless @policy.macs.include?(target_mac)
@@ -35,7 +41,9 @@ module SSHScan
35
41
  end
36
42
 
37
43
  def missing_policy_macs
38
- target_macs = @result[:mac_algorithms_server_to_client] | @result[:mac_algorithms_client_to_server]
44
+ target_macs =
45
+ @result[:mac_algorithms_server_to_client] |
46
+ @result[:mac_algorithms_client_to_server]
39
47
  outliers = []
40
48
 
41
49
  @policy.macs.each do |mac|
@@ -68,16 +76,21 @@ module SSHScan
68
76
  end
69
77
 
70
78
  def out_of_policy_compression
71
- target_compressions = @result[:compression_algorithms_server_to_client] | @result[:compression_algorithms_client_to_server]
79
+ target_compressions =
80
+ @result[:compression_algorithms_server_to_client] |
81
+ @result[:compression_algorithms_client_to_server]
72
82
  outliers = []
73
83
  target_compressions.each do |target_compression|
74
- outliers << target_compression unless @policy.compression.include?(target_compression)
84
+ outliers << target_compression unless
85
+ @policy.compression.include?(target_compression)
75
86
  end
76
87
  return outliers
77
88
  end
78
89
 
79
90
  def missing_policy_compression
80
- target_compressions = @result[:compression_algorithms_server_to_client] | @result[:compression_algorithms_client_to_server]
91
+ target_compressions =
92
+ @result[:compression_algorithms_server_to_client] |
93
+ @result[:compression_algorithms_client_to_server]
81
94
  outliers = []
82
95
 
83
96
  @policy.compression.each do |compression|
@@ -124,27 +137,63 @@ module SSHScan
124
137
  missing_policy_kex.empty? &&
125
138
  missing_policy_compression.empty? &&
126
139
  out_of_policy_auth_methods.empty? &&
127
- out_of_policy_ssh_version
140
+ !out_of_policy_ssh_version
128
141
  end
129
142
 
130
143
  def recommendations
131
144
  recommendations = []
132
145
 
133
146
  # Add these items to be compliant
134
- recommendations << "Add these Key Exchange Algos: #{missing_policy_kex.join(",")}" unless missing_policy_kex.empty?
135
- recommendations << "Add these MAC Algos: #{missing_policy_macs.join(",")}" unless missing_policy_macs.empty?
136
- recommendations << "Add these Encryption Ciphers: #{missing_policy_encryption.join(",")}" unless missing_policy_encryption.empty?
137
- recommendations << "Add these Compression Algos: #{missing_policy_compression.join(",")}" unless missing_policy_compression.empty?
147
+ if missing_policy_kex.any?
148
+ recommendations << "Add these Key Exchange Algos: \
149
+ #{missing_policy_kex.join(",")}"
150
+ end
151
+
152
+ if missing_policy_macs.any?
153
+ recommendations << "Add these MAC Algos: \
154
+ #{missing_policy_macs.join(",")}"
155
+ end
156
+
157
+ if missing_policy_encryption.any?
158
+ recommendations << "Add these Encryption Ciphers: \
159
+ #{missing_policy_encryption.join(",")}"
160
+ end
161
+
162
+ if missing_policy_compression.any?
163
+ recommendations << "Add these Compression Algos: \
164
+ #{missing_policy_compression.join(",")}"
165
+ end
138
166
 
139
167
  # Remove these items to be compliant
140
- recommendations << "Remove these Key Exchange Algos: #{out_of_policy_kex.join(", ")}" unless out_of_policy_kex.empty?
141
- recommendations << "Remove these MAC Algos: #{out_of_policy_macs.join(", ")}" unless out_of_policy_macs.empty?
142
- recommendations << "Remove these Encryption Ciphers: #{out_of_policy_encryption.join(", ")}" unless out_of_policy_encryption.empty?
143
- recommendations << "Remove these Compression Algos: #{out_of_policy_compression.join(", ")}" unless out_of_policy_compression.empty?
144
- recommendations << "Remove these Authentication Methods: #{out_of_policy_auth_methods.join(", ")}" unless out_of_policy_auth_methods.empty?
168
+ if out_of_policy_kex.any?
169
+ recommendations << "Remove these Key Exchange Algos: \
170
+ #{out_of_policy_kex.join(", ")}"
171
+ end
172
+
173
+ if out_of_policy_macs.any?
174
+ recommendations << "Remove these MAC Algos: \
175
+ #{out_of_policy_macs.join(", ")}"
176
+ end
177
+
178
+ if out_of_policy_encryption.any?
179
+ recommendations << "Remove these Encryption Ciphers: \
180
+ #{out_of_policy_encryption.join(", ")}"
181
+ end
182
+
183
+ if out_of_policy_compression.any?
184
+ recommendations << "Remove these Compression Algos: \
185
+ #{out_of_policy_compression.join(", ")}"
186
+ end
187
+
188
+ if out_of_policy_auth_methods.any?
189
+ recommendations << "Remove these Authentication Methods: \
190
+ #{out_of_policy_auth_methods.join(", ")}"
191
+ end
145
192
 
146
193
  # Update these items to be compliant
147
- recommendations << "Update your ssh version to: #{@policy.ssh_version}" if out_of_policy_ssh_version
194
+ if out_of_policy_ssh_version
195
+ recommendations << "Update your ssh version to: #{@policy.ssh_version}"
196
+ end
148
197
 
149
198
  return recommendations
150
199
  end
@@ -11,28 +11,50 @@ module SSHScan
11
11
  uint8 :message_code, :initial_value => 20
12
12
  string :cookie_string, :length => 16
13
13
  uint32 :kex_algorithms_length
14
- string :key_algorithms_string, :length => lambda { self.kex_algorithms_length }
14
+ string :key_algorithms_string, :length => lambda {
15
+ self.kex_algorithms_length
16
+ }
15
17
  uint32 :server_host_key_algorithms_length
16
- string :server_host_key_algorithms_string, :length => lambda { self.server_host_key_algorithms_length }
18
+ string :server_host_key_algorithms_string, :length => lambda {
19
+ self.server_host_key_algorithms_length
20
+ }
17
21
  uint32 :encryption_algorithms_client_to_server_length
18
- string :encryption_algorithms_client_to_server_string, :length => lambda { self.encryption_algorithms_client_to_server_length }
22
+ string :encryption_algorithms_client_to_server_string, :length => lambda {
23
+ self.encryption_algorithms_client_to_server_length
24
+ }
19
25
  uint32 :encryption_algorithms_server_to_client_length
20
- string :encryption_algorithms_server_to_client_string, :length => lambda { self.encryption_algorithms_server_to_client_length }
26
+ string :encryption_algorithms_server_to_client_string, :length => lambda {
27
+ self.encryption_algorithms_server_to_client_length
28
+ }
21
29
  uint32 :mac_algorithms_client_to_server_length
22
- string :mac_algorithms_client_to_server_string, :length => lambda { self.mac_algorithms_client_to_server_length }
30
+ string :mac_algorithms_client_to_server_string, :length => lambda {
31
+ self.mac_algorithms_client_to_server_length
32
+ }
23
33
  uint32 :mac_algorithms_server_to_client_length
24
- string :mac_algorithms_server_to_client_string, :length => lambda { self.mac_algorithms_server_to_client_length }
34
+ string :mac_algorithms_server_to_client_string, :length => lambda {
35
+ self.mac_algorithms_server_to_client_length
36
+ }
25
37
  uint32 :compression_algorithms_client_to_server_length
26
- string :compression_algorithms_client_to_server_string, :length => lambda { self.compression_algorithms_client_to_server_length }
38
+ string :compression_algorithms_client_to_server_string, :length => lambda {
39
+ self.compression_algorithms_client_to_server_length
40
+ }
27
41
  uint32 :compression_algorithms_server_to_client_length
28
- string :compression_algorithms_server_to_client_string, :length => lambda { self.compression_algorithms_server_to_client_length }
42
+ string :compression_algorithms_server_to_client_string, :length => lambda {
43
+ self.compression_algorithms_server_to_client_length
44
+ }
29
45
  uint32 :languages_client_to_server_length
30
- string :languages_client_to_server_string, :length => lambda { self.languages_client_to_server_length }
46
+ string :languages_client_to_server_string, :length => lambda {
47
+ self.languages_client_to_server_length
48
+ }
31
49
  uint32 :languages_server_to_client_length
32
- string :languages_server_to_client_string, :length => lambda { self.languages_server_to_client_length }
50
+ string :languages_server_to_client_string, :length => lambda {
51
+ self.languages_server_to_client_length
52
+ }
33
53
  uint8 :kex_first_packet_follows
34
54
  uint32 :reserved
35
- string :padding_string, :read_length => lambda { self.padding_length }
55
+ string :padding_string, :read_length => lambda {
56
+ self.padding_length
57
+ }
36
58
 
37
59
  def cookie
38
60
  self.cookie_string
@@ -183,12 +205,16 @@ module SSHScan
183
205
  :cookie => self.cookie.hexify,
184
206
  :key_algorithms => key_algorithms,
185
207
  :server_host_key_algorithms => server_host_key_algorithms,
186
- :encryption_algorithms_client_to_server => encryption_algorithms_client_to_server,
187
- :encryption_algorithms_server_to_client => encryption_algorithms_server_to_client,
208
+ :encryption_algorithms_client_to_server =>
209
+ encryption_algorithms_client_to_server,
210
+ :encryption_algorithms_server_to_client =>
211
+ encryption_algorithms_server_to_client,
188
212
  :mac_algorithms_client_to_server => mac_algorithms_client_to_server,
189
213
  :mac_algorithms_server_to_client => mac_algorithms_server_to_client,
190
- :compression_algorithms_client_to_server => compression_algorithms_client_to_server,
191
- :compression_algorithms_server_to_client => compression_algorithms_server_to_client,
214
+ :compression_algorithms_client_to_server =>
215
+ compression_algorithms_client_to_server,
216
+ :compression_algorithms_server_to_client =>
217
+ compression_algorithms_server_to_client,
192
218
  :languages_client_to_server => languages_client_to_server,
193
219
  :languages_server_to_client => languages_server_to_client,
194
220
  }
@@ -200,12 +226,18 @@ module SSHScan
200
226
  kex_init.padding = opts[:padding]
201
227
  kex_init.key_algorithms = opts[:key_algorithms]
202
228
  kex_init.server_host_key_algorithms = opts[:server_host_key_algorithms]
203
- kex_init.encryption_algorithms_client_to_server = opts[:encryption_algorithms_client_to_server]
204
- kex_init.encryption_algorithms_server_to_client = opts[:encryption_algorithms_server_to_client]
205
- kex_init.mac_algorithms_client_to_server = opts[:mac_algorithms_client_to_server]
206
- kex_init.mac_algorithms_server_to_client = opts[:mac_algorithms_server_to_client]
207
- kex_init.compression_algorithms_client_to_server = opts[:compression_algorithms_client_to_server]
208
- kex_init.compression_algorithms_server_to_client = opts[:compression_algorithms_server_to_client]
229
+ kex_init.encryption_algorithms_client_to_server =
230
+ opts[:encryption_algorithms_client_to_server]
231
+ kex_init.encryption_algorithms_server_to_client =
232
+ opts[:encryption_algorithms_server_to_client]
233
+ kex_init.mac_algorithms_client_to_server =
234
+ opts[:mac_algorithms_client_to_server]
235
+ kex_init.mac_algorithms_server_to_client =
236
+ opts[:mac_algorithms_server_to_client]
237
+ kex_init.compression_algorithms_client_to_server =
238
+ opts[:compression_algorithms_client_to_server]
239
+ kex_init.compression_algorithms_server_to_client =
240
+ opts[:compression_algorithms_server_to_client]
209
241
  kex_init.languages_client_to_server = opts[:languages_client_to_server]
210
242
  kex_init.languages_server_to_client = opts[:languages_server_to_client]
211
243
  return kex_init
@@ -1,8 +1,9 @@
1
1
  require 'socket'
2
2
  require 'ssh_scan/client'
3
3
  require 'ssh_scan/crypto'
4
- require 'ssh_scan/fingerprint_database'
4
+ #require 'ssh_scan/fingerprint_database'
5
5
  require 'net/ssh'
6
+ require 'logger'
6
7
 
7
8
  module SSHScan
8
9
  class ScanEngine
@@ -12,24 +13,30 @@ module SSHScan
12
13
  if port.nil?
13
14
  port = 22
14
15
  end
15
- timeout = opts[:timeout]
16
+ timeout = opts["timeout"]
16
17
  result = []
17
18
 
18
19
  start_time = Time.now
19
20
 
20
21
  if target.fqdn?
21
22
  if target.resolve_fqdn_as_ipv6.nil?
22
- client = SSHScan::Client.new(target.resolve_fqdn_as_ipv4.to_s, port, timeout)
23
+ client = SSHScan::Client.new(
24
+ target.resolve_fqdn_as_ipv4.to_s, port, timeout
25
+ )
23
26
  client.connect()
24
27
  result = client.get_kex_result()
25
28
  result[:hostname] = target
26
29
  return result if result.include?(:error)
27
30
  else
28
- client = SSHScan::Client.new(target.resolve_fqdn_as_ipv6.to_s, port, timeout)
31
+ client = SSHScan::Client.new(
32
+ target.resolve_fqdn_as_ipv6.to_s, port, timeout
33
+ )
29
34
  client.connect()
30
35
  result = client.get_kex_result()
31
36
  if result.include?(:error)
32
- client = SSHScan::Client.new(target.resolve_fqdn_as_ipv4.to_s, port, timeout)
37
+ client = SSHScan::Client.new(
38
+ target.resolve_fqdn_as_ipv4.to_s, port, timeout
39
+ )
33
40
  client.connect()
34
41
  result = client.get_kex_result()
35
42
  result[:hostname] = target
@@ -53,10 +60,11 @@ module SSHScan
53
60
  :paranoid => Net::SSH::Verifiers::Null.new
54
61
  )
55
62
  raise SSHScan::Error::ClosedConnection.new if net_ssh_session.closed?
56
- auth_session = Net::SSH::Authentication::Session.new(net_ssh_session, :auth_methods => ["none"])
63
+ auth_session = Net::SSH::Authentication::Session.new(
64
+ net_ssh_session, :auth_methods => ["none"]
65
+ )
57
66
  auth_session.authenticate("none", "test", "test")
58
67
  result['auth_methods'] = auth_session.allowed_auth_methods
59
- host_key = net_ssh_session.host_keys.first
60
68
  net_ssh_session.close
61
69
  rescue Net::SSH::ConnectionTimeout => e
62
70
  result[:error] = e
@@ -71,13 +79,32 @@ module SSHScan
71
79
  raise e
72
80
  end
73
81
  else
74
- pkey = SSHScan::Crypto::PublicKey.new(host_key)
75
- if pkey.is_supported?
76
- result['fingerprints'] = {
77
- "md5" => pkey.fingerprint_md5,
78
- "sha1" => pkey.fingerprint_sha1,
79
- "sha256" => pkey.fingerprint_sha256,
80
- }
82
+ result['fingerprints'] = {}
83
+ host_keys = `ssh-keyscan -t rsa,dsa #{target} 2>/dev/null`.split
84
+ host_keys_len = host_keys.length - 1
85
+
86
+ for i in 0..host_keys_len
87
+ if host_keys[i].eql? "ssh-dss"
88
+ pkey = SSHScan::Crypto::PublicKey.new(host_keys[i + 1])
89
+ result['fingerprints'].merge!({
90
+ "dsa" => {
91
+ "md5" => pkey.fingerprint_md5,
92
+ "sha1" => pkey.fingerprint_sha1,
93
+ "sha256" => pkey.fingerprint_sha256,
94
+ }
95
+ })
96
+ end
97
+
98
+ if host_keys[i].eql? "ssh-rsa"
99
+ pkey = SSHScan::Crypto::PublicKey.new(host_keys[i + 1])
100
+ result['fingerprints'].merge!({
101
+ "rsa" => {
102
+ "md5" => pkey.fingerprint_md5,
103
+ "sha1" => pkey.fingerprint_sha1,
104
+ "sha256" => pkey.fingerprint_sha256,
105
+ }
106
+ })
107
+ end
81
108
  end
82
109
  end
83
110
 
@@ -91,21 +118,20 @@ module SSHScan
91
118
  end
92
119
 
93
120
  def scan(opts)
94
- sockets = opts[:sockets]
95
- threads = opts[:threads] || 5
96
- logger = opts[:logger]
121
+ sockets = opts["sockets"]
122
+ threads = opts["threads"] || 5
123
+ logger = opts["logger"] || Logger.new(STDOUT)
97
124
 
98
125
  results = []
99
126
 
100
127
  work_queue = Queue.new
128
+
101
129
  sockets.each {|x| work_queue.push x }
102
- workers = (0...threads).map do |worker_num|
130
+ workers = (0...threads).map do
103
131
  Thread.new do
104
132
  begin
105
133
  while socket = work_queue.pop(true)
106
- logger.info("Started ssh_scan of #{socket}")
107
134
  results << scan_target(socket, opts)
108
- logger.info("Completed ssh_scan of #{socket}")
109
135
  end
110
136
  rescue ThreadError => e
111
137
  raise e unless e.to_s.match(/queue empty/)
@@ -115,35 +141,41 @@ module SSHScan
115
141
  workers.map(&:join)
116
142
 
117
143
  # Add all the fingerprints to our peristent FingerprintDatabase
118
- fingerprint_db = SSHScan::FingerprintDatabase.new(opts[:fingerprint_database])
119
- results.each do |result|
120
- fingerprint_db.clear_fingerprints(result[:ip])
121
- if result['fingerprints']
122
- result['fingerprints'].values.each do |fingerprint|
123
- fingerprint_db.add_fingerprint(fingerprint, result[:ip])
124
- end
125
- end
126
- end
144
+ # fingerprint_db = SSHScan::FingerprintDatabase.new(
145
+ # opts[:fingerprint_database]
146
+ # )
147
+ # results.each do |result|
148
+ # fingerprint_db.clear_fingerprints(result[:ip])
149
+ # if result['fingerprints']
150
+ # result['fingerprints'].values.each do |host_key_algo|
151
+ # host_key_algo.values.each do |fingerprint|
152
+ # fingerprint_db.add_fingerprint(fingerprint, result[:ip])
153
+ # end
154
+ # end
155
+ # end
156
+ # end
127
157
 
128
158
  # Decorate all the results with duplicate keys
129
- results.each do |result|
130
- if result['fingerprints']
131
- ip = result[:ip]
132
- result['duplicate_host_key_ips'] = []
133
- result['fingerprints'].values.each do |fingerprint|
134
- fingerprint_db.find_fingerprints(fingerprint).each do |other_ip|
135
- next if ip == other_ip
136
- result['duplicate_host_key_ips'] << other_ip
137
- end
138
- end
139
- result['duplicate_host_key_ips'].uniq!
140
- end
141
- end
159
+ # results.each do |result|
160
+ # if result['fingerprints']
161
+ # ip = result[:ip]
162
+ # result['duplicate_host_key_ips'] = []
163
+ # result['fingerprints'].values.each do |host_key_algo|
164
+ # host_key_algo.values.each do |fingerprint|
165
+ # fingerprint_db.find_fingerprints(fingerprint).each do |other_ip|
166
+ # next if ip == other_ip
167
+ # result['duplicate_host_key_ips'] << other_ip
168
+ # end
169
+ # end
170
+ # end
171
+ # result['duplicate_host_key_ips'].uniq!
172
+ # end
173
+ # end
142
174
 
143
175
  # Decorate all the results with compliance information
144
176
  results.each do |result|
145
177
  # Do this only when we have all the information we need
146
- if !opts[:policy_file].nil? &&
178
+ if !opts["policy"].nil? &&
147
179
  !result[:key_algorithms].nil? &&
148
180
  !result[:server_host_key_algorithms].nil? &&
149
181
  !result[:encryption_algorithms_client_to_server].nil? &&
@@ -154,7 +186,9 @@ module SSHScan
154
186
  !result[:compression_algorithms_server_to_client].nil? &&
155
187
  !result[:languages_client_to_server].nil? &&
156
188
  !result[:languages_server_to_client].nil?
157
- policy_mgr = SSHScan::PolicyManager.new(result, opts[:policy_file])
189
+
190
+ policy = SSHScan::Policy.from_file(opts["policy"])
191
+ policy_mgr = SSHScan::PolicyManager.new(result, policy)
158
192
  result['compliance'] = policy_mgr.compliance_results
159
193
  end
160
194
  end