ssh_scan 0.0.14 → 0.0.15

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3d541b6a665e8adbd86013e68cea159cb42a514b
4
- data.tar.gz: cbbb3ba9b9e169091ca8db560ccfb06780781f39
3
+ metadata.gz: c5d764d9a2898d145c0f46eee1c59bb0c6e7bcde
4
+ data.tar.gz: 9bd1e38f1e4c715bf01a9526b85a5cf4d5c7961f
5
5
  SHA512:
6
- metadata.gz: 70218de598e768b46841299ebe891980ece0080792d2eac5fcf8d879e13c15c841a817bbf148968cf6ec2117d232c80c7f676eada7b1d812014109bfe5b31c34
7
- data.tar.gz: e1fe1bdb16ea13623741607d54e3102af8a4e26c0b94adaebbbc90736c5587a3b2b427057e866cf73e3a66ec7cb58c52a67f6f9ec39a196f6e3cd0bb3c35bb95
6
+ metadata.gz: d9dca78fe553facec68df4241f43ed13537ef08d5b39f9d50ddafa50e97ba0c24d76a836f527c677e7cd90092be970604a3729b0348e06dfd28daefb4aef34b1
7
+ data.tar.gz: 67329c5b6e9c652b4572c9d1085be7cf220d48cafe19cf4dfa2827d8849ea5e43afcc284f79638cd1fdb03fca79677b1b662c3bef64f61765e4ab51f442475a4
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  *.gem
2
2
  *.rbc
3
+ *.db
3
4
  /.config
4
5
  /coverage/
5
6
  /InstalledFiles
@@ -34,3 +35,4 @@ build/
34
35
 
35
36
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
36
37
  .rvmrc
38
+ gh-pages/
data/README.md CHANGED
@@ -59,22 +59,23 @@ bundle install
59
59
 
60
60
  Run `ssh_scan -h` to get this
61
61
 
62
- ssh_scan v0.0.10 (https://github.com/mozilla/ssh_scan)
62
+ ssh_scan v0.0.15 (https://github.com/mozilla/ssh_scan)
63
63
 
64
64
  Usage: ssh_scan [options]
65
65
  -t, --target [IP/Range/Hostname] IP/Ranges/Hostname to scan
66
66
  -f, --file [FilePath] File Path of the file containing IP/Range/Hostnames to scan
67
67
  -T, --timeout [seconds] Timeout per connect after which ssh_scan gives up on the host
68
- -o, --output [FilePath] File to write JSON output to
68
+ -L, --logger [Log File Path] Enable logger
69
69
  -O, --from_json [FilePath] File to read JSON output from
70
+ -o, --output [FilePath] File to write JSON output to
70
71
  -p, --port [PORT] Port (Default: 22)
71
72
  -P, --policy [FILE] Custom policy file (Default: Mozilla Modern)
72
73
  --threads [NUMBER] Number of worker threads (Default: 5)
74
+ --fingerprint-db [FILE] File location of fingerprint database (Default: ./fingerprints.db)
73
75
  -u, --unit-test [FILE] Throw appropriate exit codes based on compliance status
76
+ -V, --verbosity Set the logger level (Accepted Params: INFO, DEBUG, WARN, ERROR, FATAL)
74
77
  -v, --version Display just version info
75
78
  -h, --help Show this message
76
- -L, --logger[Log File Path] Enable logger and set the log file
77
- -V, --verbosity Set the logger level (Params: INFO, DEBUG, WARN, ERROR, FATAL)
78
79
 
79
80
  Examples:
80
81
 
data/bin/ssh_scan CHANGED
@@ -18,6 +18,7 @@ options = {
18
18
  :threads => 5,
19
19
  :verbosity => nil,
20
20
  :logger => Logger.new(STDERR),
21
+ :fingerprint_database => "./fingerprints.db"
21
22
  }
22
23
 
23
24
  target_parser = SSHScan::TargetParser.new()
@@ -107,13 +108,18 @@ opt_parser = OptionParser.new do |opts|
107
108
  options[:threads] = threads.to_i
108
109
  end
109
110
 
111
+ opts.on("--fingerprint-db [FILE]",
112
+ "File location of fingerprint database (Default: ./fingerprints.db)") do |fingerprint_db|
113
+ options[:fingerprint_database] = fingerprint_db
114
+ end
115
+
110
116
  opts.on("-u", "--unit-test [FILE]",
111
117
  "Throw appropriate exit codes based on compliance status") do
112
118
  options[:unit_test] = true
113
119
  end
114
120
 
115
121
  opts.on("-V", "--verbosity",
116
- "Set the logger level (Accpeted Params: INFO, DEBUG, WARN, ERROR, FATAL)") do |verbosity|
122
+ "Set the logger level (Accepted Params: INFO, DEBUG, WARN, ERROR, FATAL)") do |verbosity|
117
123
  options[:logger].level = case options[:verbosity]
118
124
  when "INFO" then Logger::INFO
119
125
  when "DEBUG" then Logger::DEBUG
@@ -183,6 +189,14 @@ unless File.exists?(options[:policy])
183
189
  exit 1
184
190
  end
185
191
 
192
+ # Check to see if we're running the latest released version
193
+ update = SSHScan::Update.new
194
+ if update.newer_gem_available?
195
+ options[:logger].warn("You're NOT using the latest version of ssh_scan, try 'gem update ssh_scan' to get the latest")
196
+ else
197
+ options[:logger].info("You're using the latest version of ssh_scan #{SSHScan::VERSION}")
198
+ end
199
+
186
200
  options[:policy_file] = SSHScan::Policy.from_file(options[:policy])
187
201
 
188
202
  # Perform scan and get results
data/lib/ssh_scan.rb CHANGED
@@ -11,6 +11,7 @@ require 'ssh_scan/policy_manager'
11
11
  require 'ssh_scan/protocol'
12
12
  require 'ssh_scan/scan_engine'
13
13
  require 'ssh_scan/target_parser'
14
+ require 'ssh_scan/update'
14
15
 
15
16
  #Monkey Patches
16
17
  require 'string_ext'
@@ -71,20 +71,32 @@ module SSHScan
71
71
  result[:ssh_lib] = @server_banner.ssh_lib_guess.common
72
72
  result[:ssh_lib_cpe] = @server_banner.ssh_lib_guess.cpe
73
73
 
74
- @sock.write(kex_init_raw)
75
- resp = @sock.read(4)
74
+ begin
75
+ @sock.write(kex_init_raw)
76
+ resp = @sock.read(4)
76
77
 
77
- if resp.nil?
78
- @error = SSHScan::Error::NoKexResponse.new("service did not respond to our kex init request")
79
- @sock = nil
80
- return result
81
- end
78
+ if resp.nil?
79
+ result[:error] = SSHScan::Error::NoKexResponse.new("service did not respond to our kex init request")
80
+ @sock = nil
81
+ return result
82
+ end
82
83
 
83
- resp += @sock.read(resp.unpack("N").first)
84
- @sock.close
84
+ resp += @sock.read(resp.unpack("N").first)
85
+ @sock.close
85
86
 
86
- kex_exchange_init = SSHScan::KeyExchangeInit.read(resp)
87
- result.merge!(kex_exchange_init.to_hash)
87
+ kex_exchange_init = SSHScan::KeyExchangeInit.read(resp)
88
+ result.merge!(kex_exchange_init.to_hash)
89
+ rescue Errno::ETIMEDOUT => e
90
+ @error = SSHScan::Error::ConnectTimeout.new(e.message)
91
+ @sock = nil
92
+ rescue Errno::ECONNREFUSED,
93
+ Errno::ENETUNREACH,
94
+ Errno::ECONNRESET,
95
+ Errno::EACCES,
96
+ Errno::EHOSTUNREACH => e
97
+ result[:error] = SSHScan::Error::NoKexResponse.new("service did not respond to our kex init request")
98
+ @sock = nil
99
+ end
88
100
 
89
101
  return result
90
102
  end
@@ -0,0 +1,39 @@
1
+ require 'sqlite3'
2
+
3
+ module SSHScan
4
+ class FingerprintDatabase
5
+ def initialize(database_name)
6
+ if File.exists?(database_name)
7
+ @db = ::SQLite3::Database.open(database_name)
8
+ else
9
+ @db = ::SQLite3::Database.new(database_name)
10
+ self.create_schema
11
+ end
12
+ end
13
+
14
+ def create_schema
15
+ @db.execute <<-SQL
16
+ create table fingerprints (
17
+ fingerprint varchar(100),
18
+ ip varchar(100)
19
+ );
20
+ SQL
21
+ end
22
+
23
+ def clear_fingerprints(ip)
24
+ @db.execute "delete from fingerprints where ip like ( ? )", [ip]
25
+ end
26
+
27
+ def add_fingerprint(fingerprint, ip)
28
+ @db.execute "insert into fingerprints values ( ?, ? )", [fingerprint, ip]
29
+ end
30
+
31
+ def find_fingerprints(fingerprint)
32
+ ips = []
33
+ @db.execute( "select * from fingerprints where fingerprint like ( ? )", [fingerprint] ) do |row|
34
+ ips << row[1]
35
+ end
36
+ return ips
37
+ end
38
+ end
39
+ end
@@ -146,7 +146,8 @@ module SSHScan
146
146
  "6.6p1-2ubuntu2.2",
147
147
  "6.6p1-2ubuntu2",
148
148
  "6.6p1-2ubuntu1",
149
- "6.2p2-6ubuntu1"],
149
+ "6.2p2-6ubuntu1",
150
+ "6.6.1p1 Ubuntu-8"],
150
151
  "14.10" => ["6.6p1-5build1"],
151
152
  "15.04" =>
152
153
  ["6.7p1-5ubuntu1.4",
@@ -1,6 +1,7 @@
1
1
  require 'socket'
2
2
  require 'ssh_scan/client'
3
3
  require 'ssh_scan/crypto'
4
+ require 'ssh_scan/fingerprint_database'
4
5
  require 'net/ssh'
5
6
 
6
7
  module SSHScan
@@ -11,7 +12,6 @@ module SSHScan
11
12
  if port.nil?
12
13
  port = 22
13
14
  end
14
- policy = opts[:policy_file]
15
15
  timeout = opts[:timeout]
16
16
  result = []
17
17
 
@@ -46,7 +46,12 @@ module SSHScan
46
46
 
47
47
  # Connect and get results (Net-SSH)
48
48
  begin
49
- net_ssh_session = Net::SSH::Transport::Session.new(target, :port => port, :timeout => timeout)
49
+ net_ssh_session = Net::SSH::Transport::Session.new(
50
+ target,
51
+ :port => port,
52
+ :timeout => timeout,
53
+ :paranoid => Net::SSH::Verifiers::Null.new
54
+ )
50
55
  raise SSHScan::Error::ClosedConnection.new if net_ssh_session.closed?
51
56
  auth_session = Net::SSH::Authentication::Session.new(net_ssh_session, :auth_methods => ["none"])
52
57
  auth_session.authenticate("none", "test", "test")
@@ -76,25 +81,8 @@ module SSHScan
76
81
  end
77
82
  end
78
83
 
79
- # Do this only when no errors were reported
80
- if !policy.nil? &&
81
- !result[:key_algorithms].nil? &&
82
- !result[:server_host_key_algorithms].nil? &&
83
- !result[:encryption_algorithms_client_to_server].nil? &&
84
- !result[:encryption_algorithms_server_to_client].nil? &&
85
- !result[:mac_algorithms_client_to_server].nil? &&
86
- !result[:mac_algorithms_server_to_client].nil? &&
87
- !result[:compression_algorithms_client_to_server].nil? &&
88
- !result[:compression_algorithms_server_to_client].nil? &&
89
- !result[:languages_client_to_server].nil? &&
90
- !result[:languages_server_to_client].nil?
91
- policy_mgr = SSHScan::PolicyManager.new(result, policy)
92
- result['compliance'] = policy_mgr.compliance_results
93
- end
94
-
95
84
  # Add scan times
96
85
  end_time = Time.now
97
-
98
86
  result['start_time'] = start_time.to_s
99
87
  result['end_time'] = end_time.to_s
100
88
  result['scan_duration_seconds'] = end_time - start_time
@@ -126,6 +114,51 @@ module SSHScan
126
114
  end
127
115
  workers.map(&:join)
128
116
 
117
+ # 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
127
+
128
+ # 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
142
+
143
+ # Decorate all the results with compliance information
144
+ results.each do |result|
145
+ # Do this only when we have all the information we need
146
+ if !opts[:policy_file].nil? &&
147
+ !result[:key_algorithms].nil? &&
148
+ !result[:server_host_key_algorithms].nil? &&
149
+ !result[:encryption_algorithms_client_to_server].nil? &&
150
+ !result[:encryption_algorithms_server_to_client].nil? &&
151
+ !result[:mac_algorithms_client_to_server].nil? &&
152
+ !result[:mac_algorithms_server_to_client].nil? &&
153
+ !result[:compression_algorithms_client_to_server].nil? &&
154
+ !result[:compression_algorithms_server_to_client].nil? &&
155
+ !result[:languages_client_to_server].nil? &&
156
+ !result[:languages_server_to_client].nil?
157
+ policy_mgr = SSHScan::PolicyManager.new(result, opts[:policy_file])
158
+ result['compliance'] = policy_mgr.compliance_results
159
+ end
160
+ end
161
+
129
162
  return results
130
163
  end
131
164
  end
@@ -0,0 +1,64 @@
1
+ require 'ssh_scan/os'
2
+ require 'ssh_scan/ssh_lib'
3
+ require 'ssh_scan/version'
4
+ require 'net/http'
5
+
6
+ module SSHScan
7
+ class Update
8
+ def next_patch_version(version = SSHScan::VERSION)
9
+ major, minor, patch = version.split(".")
10
+ patch_num = patch.to_i
11
+ patch_num += 1
12
+
13
+ return [major, minor, patch_num.to_s].join(".")
14
+ end
15
+
16
+ def next_minor_version(version = SSHScan::VERSION)
17
+ major, minor, patch = version.split(".")
18
+ minor_num = minor.to_i
19
+ minor_num += 1
20
+
21
+ return [major, minor_num.to_s, "0"].join(".")
22
+ end
23
+
24
+ def next_major_version(version = SSHScan::VERSION)
25
+ major, minor, patch = version.split(".")
26
+ major_num = major.to_i
27
+ major_num += 1
28
+
29
+ return [major_num.to_s, "0", "0"].join(".")
30
+ end
31
+
32
+ def gem_exists?(version = SSHScan::VERSION)
33
+ uri = URI("https://rubygems.org/gems/ssh_scan/versions/#{version}")
34
+
35
+ begin
36
+ res = Net::HTTP.get_response(uri)
37
+ rescue
38
+ return false
39
+ end
40
+
41
+ if res.code != "200"
42
+ return false
43
+ else
44
+ return true
45
+ end
46
+ end
47
+
48
+ def newer_gem_available?(version = SSHScan::VERSION)
49
+ if gem_exists?(next_patch_version(version))
50
+ return true
51
+ end
52
+
53
+ if gem_exists?(next_minor_version(version))
54
+ return true
55
+ end
56
+
57
+ if gem_exists?(next_major_version(version))
58
+ return true
59
+ end
60
+
61
+ return false
62
+ end
63
+ end
64
+ end
@@ -1,3 +1,3 @@
1
1
  module SSHScan
2
- VERSION = '0.0.14'
2
+ VERSION = '0.0.15'
3
3
  end
data/ssh_scan.gemspec CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
29
29
  s.add_dependency('bindata', '~> 2.0')
30
30
  s.add_dependency('netaddr')
31
31
  s.add_dependency('net-ssh')
32
+ s.add_dependency('sqlite3')
32
33
  s.add_development_dependency('pry')
33
34
  s.add_development_dependency('rspec', '~> 3.0')
34
35
  s.add_development_dependency('rspec-its', '~> 1.2')
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.14
4
+ version: 0.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Claudius
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-09-16 00:00:00.000000000 Z
12
+ date: 2016-09-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bindata
@@ -53,6 +53,20 @@ dependencies:
53
53
  - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: sqlite3
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: pry
58
72
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +150,7 @@ files:
136
150
  - lib/ssh_scan/error/disconnected.rb
137
151
  - lib/ssh_scan/error/no_banner.rb
138
152
  - lib/ssh_scan/error/no_kex_response.rb
153
+ - lib/ssh_scan/fingerprint_database.rb
139
154
  - lib/ssh_scan/os.rb
140
155
  - lib/ssh_scan/os/centos.rb
141
156
  - lib/ssh_scan/os/cisco.rb
@@ -160,6 +175,7 @@ files:
160
175
  - lib/ssh_scan/ssh_lib/rosssh.rb
161
176
  - lib/ssh_scan/ssh_lib/unknown.rb
162
177
  - lib/ssh_scan/target_parser.rb
178
+ - lib/ssh_scan/update.rb
163
179
  - lib/ssh_scan/version.rb
164
180
  - lib/string_ext.rb
165
181
  - policies/mozilla_intermediate.yml