ssh_scan 0.0.16 → 0.0.17.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -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