ssh_scan 0.0.8 → 0.0.9.beta.1

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: 1a02e7aa137511342eb655ddb1040cfbe8f39e47
4
- data.tar.gz: 0db6be22ce2c16b3d389c742142f97b1288c422f
3
+ metadata.gz: c1c87c8107e90d90c415e7bc82fa379a8b0f53b4
4
+ data.tar.gz: fe3e44a14d6eb1f93a8a86423150520c187dfb70
5
5
  SHA512:
6
- metadata.gz: 298a42ca4f082a1c733e30189988506b3b99d0e08aeb406daee23047242a5e4e73dd44e0845fd36ae13b3a67b59c0db6fe35b41840a08e7931833beba90c9ed6
7
- data.tar.gz: abb0db799fe454f328e7043f9479427e09fc76360992401f4cf90e0b9db5068589cd14569d2921c56930520497a02171945bbcc08940ef1e46a837c1717072c1
6
+ metadata.gz: c0fe202b82f180482f8c56690b89f26a99966a8c1de2f454e29c5d4bc8490a796fdd5ae88f36a74eed42cb1c438fa5c579662571042b9e3e35157e2c4fcd171d
7
+ data.tar.gz: 3efc0086e634f5c2119e5de4e4b118844c0c62ba2eb3fbe912e48c51a64c44c5812cb7763e18f59817e1bb1721cb6b647819f733359aa250c18726be3f05573a
data/.travis.yml CHANGED
@@ -1,5 +1,7 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 2.3.0
4
+ - 2.2.0
3
5
  - 2.1.3
4
6
  - 2.1.0
5
7
  - 2.0.0
data/README.md CHANGED
@@ -8,26 +8,41 @@ A SSH configuration and policy scanner
8
8
 
9
9
  ## Key Benefits
10
10
 
11
- - **Minimal Dependancies** - Uses native Ruby and BinData to do it's work, no heavy dependancies.
11
+ - **Minimal Dependancies** - Uses native Ruby and BinData to do its work, no heavy dependancies.
12
12
  - **Not Just a Script** - Implementation is portable for use in another project or for automation of tasks.
13
- - **Simple** - Just point ssh_scan at an SSH service and get a JSON report of what is supports and it's policy status
13
+ - **Simple** - Just point `ssh_scan` at an SSH service and get a JSON report of what it supports and its policy status.
14
14
  - **Configurable** - Make your own custom policies that fit your unique policy requirements.
15
15
 
16
16
  ## Setup
17
17
 
18
- To install as a gem, type
18
+ To install as a gem, type:
19
19
 
20
20
  ```bash
21
21
  gem install ssh_scan
22
22
  ssh_scan
23
23
  ```
24
24
 
25
- To install from source, type
25
+ To install from source, type:
26
26
 
27
27
  ```bash
28
+ # clone repo
28
29
  git clone https://github.com/mozilla/ssh_scan.git
29
30
  cd ssh_scan
30
- gem install bindata
31
+
32
+ # install rvm,
33
+ # you might have to provide root to install missing packages
34
+ gpg2 --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
35
+ curl -sSL https://get.rvm.io | bash -s stable
36
+
37
+ # install Ruby 2.1.3 with rvm,
38
+ # again, you might have to install missing devel packages
39
+ rvm install 2.1.3
40
+ rvm use 2.1.3
41
+
42
+ # resolve dependencies
43
+ gem install bundler
44
+ bundle install
45
+
31
46
  ./bin/ssh_scan
32
47
  ```
33
48
 
@@ -54,32 +69,29 @@ Run `ssh_scan -h` to get this
54
69
  ssh_scan -t 192.168.1.1 -P custom_policy.yml
55
70
  ssh_scan -t 192.168.1.1 --unit-test -P custom_policy.yml
56
71
 
57
- See here for [example video](https://asciinema.org/a/7pliiw5zqhj7eqvz7q437u6vx)
58
-
59
- See here for [example output](https://github.com/mozilla/ssh_scan/blob/master/examples/192.168.1.1.json)
60
-
61
- See here for [example policies](https://github.com/mozilla/ssh_scan/blob/master/policies)
72
+ - See here for [example video](https://asciinema.org/a/7pliiw5zqhj7eqvz7q437u6vx)
73
+ - See here for [example output](https://github.com/mozilla/ssh_scan/blob/master/examples/192.168.1.1.json)
74
+ - See here for [example policies](https://github.com/mozilla/ssh_scan/blob/master/policies)
62
75
 
63
76
  ## Rubies Supported
64
77
 
65
78
  This project is integrated with [travis-ci](http://about.travis-ci.org/) and is regularly tested to work with the following rubies:
66
79
 
80
+ * [ruby-head](https://github.com/ruby/ruby)
81
+ * [2.3.0](https://github.com/ruby/ruby/tree/ruby_2_1)
82
+ * [2.2.0](https://github.com/ruby/ruby/tree/ruby_2_1)
67
83
  * [2.1.3](https://github.com/ruby/ruby/tree/ruby_2_1)
68
84
  * [2.1.0](https://github.com/ruby/ruby/tree/ruby_2_1)
69
85
  * [2.0.0](https://github.com/ruby/ruby/tree/ruby_2_0_0)
70
- * [1.9.3](https://github.com/ruby/ruby/tree/ruby_1_9_3)
71
- * [ruby-head](https://github.com/ruby/ruby)
72
- * [jruby-head](http://jruby.org/)
73
- * [jruby-19mode](http://jruby.org/)
74
86
 
75
87
  To checkout the current build status for these rubies, click [here](https://travis-ci.org/#!/mozilla/ssh_scan).
76
88
 
77
89
  ## Contributing
78
90
 
79
- If you are interested in contributing to this project, please see [CONTRIBUTING.md](https://github.com/mozilla/ssh_scan/blob/master/CONTRIBUTING.md)
91
+ If you are interested in contributing to this project, please see [CONTRIBUTING.md](https://github.com/mozilla/ssh_scan/blob/master/CONTRIBUTING.md).
80
92
 
81
93
  ## Credits
82
94
 
83
95
  **Sources of Inspiration for ssh_scan**
84
96
 
85
- - [**Mozilla OpenSSH Security Guide**](https://wiki.mozilla.org/Security/Guidelines/OpenSSH) - For providing a sane baseline policy recommendation for SSH configuration parameters (eg. Ciphers, Macs, and KexAlgos).
97
+ - [**Mozilla OpenSSH Security Guide**](https://wiki.mozilla.org/Security/Guidelines/OpenSSH) - For providing a sane baseline policy recommendation for SSH configuration parameters (eg. Ciphers, MACs, and KexAlgos).
data/bin/ssh_scan CHANGED
@@ -5,10 +5,11 @@ $:.unshift File.join(File.dirname(__FILE__), "../lib")
5
5
 
6
6
  require 'ssh_scan'
7
7
  require 'optparse'
8
+ require 'netaddr'
8
9
 
9
10
  #Default options
10
11
  options = {
11
- :target => nil,
12
+ :targets => [],
12
13
  :port => 22,
13
14
  :policy => File.expand_path("../../policies/mozilla_modern.yml", __FILE__),
14
15
  :unit_test => false
@@ -17,9 +18,26 @@ opt_parser = OptionParser.new do |opts|
17
18
  opts.banner = "ssh_scan v#{SSHScan::VERSION} (https://github.com/mozilla/ssh_scan)\n\n" +
18
19
  "Usage: ssh_scan [options]"
19
20
 
20
- opts.on("-t", "--target [IP/Hostname]",
21
- "IP/Hostname (IPv4/IPv6/FQDNs)") do |ip|
22
- options[:target] = ip
21
+ opts.on("-t", "--target [IP/Hostname]", Array,
22
+ "IP/Hostname (IPv4/IPv6/FQDNs)") do |ips|
23
+ ips.each do |ip|
24
+ if ip.fqdn?
25
+ options[:targets] += [ip]
26
+ else
27
+ options[:targets] += NetAddr::CIDR.create(ip).enumerate
28
+ end
29
+ end
30
+ end
31
+
32
+ opts.on("-f", "--file [FilePath]",
33
+ "File Path of the file containing IPs") do |file|
34
+ txt = open(file)
35
+ options[:targets] = txt.read.chomp.split(',')
36
+ end
37
+
38
+ opts.on("-o", "--outputFile [FilePath]",
39
+ "Writing JSON documents to disk") do |file|
40
+ $stdout.reopen(file, "w")
23
41
  end
24
42
 
25
43
  opts.on("-p", "--port [PORT]",
@@ -49,6 +67,8 @@ opt_parser = OptionParser.new do |opts|
49
67
  puts "\n ssh_scan -t 192.168.1.1"
50
68
  puts " ssh_scan -t server.example.com"
51
69
  puts " ssh_scan -t ::1"
70
+ puts " ssh_scan -f filePath"
71
+ puts " ssh_scan -o filePath"
52
72
  puts " ssh_scan -t 192.168.1.1 -p 22222"
53
73
  puts " ssh_scan -t 192.168.1.1 -P custom_policy.yml"
54
74
  puts " ssh_scan -t 192.168.1.1 --unit-test -P custom_policy.yml"
@@ -59,16 +79,18 @@ end
59
79
 
60
80
  opt_parser.parse!
61
81
 
62
- if options[:target].nil?
82
+ if options[:targets].nil?
63
83
  puts opt_parser.help
64
84
  puts "\nReason: no target specified"
65
85
  exit
66
86
  end
67
87
 
68
- unless options[:target].ip_addr? || options[:target].fqdn?
69
- puts opt_parser.help
70
- puts "\nReason: #{options[:target]} is not a valid target"
71
- exit
88
+ options[:targets].each do |target|
89
+ unless target.ip_addr? || target.fqdn?
90
+ puts opt_parser.help
91
+ puts "\nReason: #{options[:targets]} is not a valid target"
92
+ exit
93
+ end
72
94
  end
73
95
 
74
96
  unless (0..65535).include?(options[:port])
@@ -87,12 +109,14 @@ options[:policy_file] = SSHScan::Policy.from_file(options[:policy])
87
109
 
88
110
  # Perform scan and get results
89
111
  scan_engine = SSHScan::ScanEngine.new()
90
- result = scan_engine.scan(options)
112
+ results = scan_engine.scan(options)
91
113
 
92
- puts JSON.pretty_generate(result)
114
+ puts JSON.pretty_generate(results)
93
115
 
94
- if result["compliance"] && result["compliance"][:compliant] == false
95
- exit 1 #non-zero means a false
96
- else
97
- exit 0 #non-zero means pass
116
+ results.each do |result|
117
+ if result["compliance"] && result["compliance"][:compliant] == false
118
+ exit 1 #non-zero means a false
119
+ else
120
+ exit 0 #non-zero means pass
121
+ end
98
122
  end
@@ -22,7 +22,7 @@ module SSHScan
22
22
  def ssh_lib_guess()
23
23
  case @string
24
24
  when /OpenSSH/i
25
- return SSHScan::SSHLib::OpenSSH.new()
25
+ return SSHScan::SSHLib::OpenSSH.new(@string)
26
26
  when /LibSSH/i
27
27
  return SSHScan::SSHLib::LibSSH.new()
28
28
  else
@@ -36,10 +36,14 @@ module SSHScan
36
36
  return SSHScan::OS::Ubuntu.new
37
37
  when /CentOS/i
38
38
  return SSHScan::OS::CentOS.new
39
+ when /RHEL|RedHat/i
40
+ return SSHScan::OS::RedHat.new
39
41
  when /FreeBSD/i
40
42
  return SSHScan::OS::FreeBSD.new
41
43
  when /Debian/i
42
44
  return SSHScan::OS::Debian.new
45
+ when /Windows/i
46
+ return SSHScan::OS::Windows.new
43
47
  else
44
48
  return SSHScan::OS::Unknown.new
45
49
  end
@@ -0,0 +1,13 @@
1
+ module SSHScan
2
+ module OS
3
+ class RedHat
4
+ def common
5
+ "redhat"
6
+ end
7
+
8
+ def cpe
9
+ "o:redhat:redhat"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -6,7 +6,7 @@ module SSHScan
6
6
  end
7
7
 
8
8
  def cpe
9
- "o:canonical:freebsd"
9
+ "o:canonical:ubuntu"
10
10
  end
11
11
  end
12
12
  end
@@ -0,0 +1,13 @@
1
+ module SSHScan
2
+ module OS
3
+ class Windows
4
+ def common
5
+ "windows"
6
+ end
7
+
8
+ def cpe
9
+ "o:microsoft:windows"
10
+ end
11
+ end
12
+ end
13
+ end
data/lib/ssh_scan/os.rb CHANGED
@@ -2,4 +2,6 @@ require 'ssh_scan/os/centos'
2
2
  require 'ssh_scan/os/debian'
3
3
  require 'ssh_scan/os/freebsd'
4
4
  require 'ssh_scan/os/ubuntu'
5
+ require 'ssh_scan/os/windows'
6
+ require 'ssh_scan/os/redhat'
5
7
  require 'ssh_scan/os/unknown'
@@ -103,10 +103,10 @@ module SSHScan
103
103
  recommendations = []
104
104
 
105
105
  # Add these items to be compliant
106
- recommendations << "Add these Key Exchange Algos: #{missing_policy_kex.join(", ")}" unless missing_policy_kex.empty?
107
- recommendations << "Add these MAC Algos: #{missing_policy_macs.join(", ")}" unless missing_policy_macs.empty?
108
- recommendations << "Add these Encryption Ciphers: #{missing_policy_encryption.join(", ")}" unless missing_policy_encryption.empty?
109
- recommendations << "Add these Compression Algos: #{missing_policy_compression.join(", ")}" unless missing_policy_compression.empty?
106
+ recommendations << "Add these Key Exchange Algos: #{missing_policy_kex.join(",")}" unless missing_policy_kex.empty?
107
+ recommendations << "Add these MAC Algos: #{missing_policy_macs.join(",")}" unless missing_policy_macs.empty?
108
+ recommendations << "Add these Encryption Ciphers: #{missing_policy_encryption.join(",")}" unless missing_policy_encryption.empty?
109
+ recommendations << "Add these Compression Algos: #{missing_policy_compression.join(",")}" unless missing_policy_compression.empty?
110
110
 
111
111
  # Remove these items to be compliant
112
112
  recommendations << "Remove these Key Exchange Algos: #{out_of_policy_kex.join(", ")}" unless out_of_policy_kex.empty?
@@ -6,49 +6,56 @@ module SSHScan
6
6
  class ScanEngine
7
7
 
8
8
  def scan(opts)
9
- target = opts[:target]
9
+ targets = opts[:targets]
10
10
  port = opts[:port]
11
11
  policy = opts[:policy_file]
12
12
 
13
13
  # Connect and get results (native)
14
- client = SSHScan::Client.new(target, port)
15
- client.connect()
16
- result = client.get_kex_result()
17
-
18
- # Connect and get results (Net-SSH)
19
- net_ssh_session = Net::SSH::Transport::Session.new(target)
20
- auth_session = Net::SSH::Authentication::Session.new(net_ssh_session, :auth_methods => ["none"])
21
- auth_session.authenticate("none", "test", "test")
22
- result['auth_methods'] = auth_session.allowed_auth_methods
23
- host_key = net_ssh_session.host_keys.first
24
- net_ssh_session.close
25
-
26
- # only supporting RSA for the moment
27
- unless OpenSSL::PKey::RSA
28
- raise "Unknown host key type, need to add this host_key type"
14
+ result = []
15
+ targets.each_with_index do |target, index|
16
+ client = SSHScan::Client.new(target, port)
17
+ client.connect()
18
+ result.push(client.get_kex_result())
19
+
20
+ # Connect and get results (Net-SSH)
21
+ net_ssh_session = Net::SSH::Transport::Session.new(target, :port => port)
22
+ auth_session = Net::SSH::Authentication::Session.new(net_ssh_session, :auth_methods => ["none"])
23
+ auth_session.authenticate("none", "test", "test")
24
+ result[index]['auth_methods'] = auth_session.allowed_auth_methods
25
+ host_key = net_ssh_session.host_keys.first
26
+ net_ssh_session.close
27
+
28
+ # only supporting RSA for the moment
29
+ unless host_key.is_a?(OpenSSL::PKey::RSA)
30
+ raise "Unknown host key type, need to add this host_key type"
31
+ end
32
+
33
+ # only supporting RSA for the moment
34
+ unless OpenSSL::PKey::RSA
35
+ raise "Unknown host key type, need to add this host_key type"
36
+ end
37
+
38
+ data_string = OpenSSL::ASN1::Sequence([
39
+ OpenSSL::ASN1::Integer.new(host_key.public_key.n),
40
+ OpenSSL::ASN1::Integer.new(host_key.public_key.e)
41
+ ])
42
+
43
+ fingerprint_md5 = OpenSSL::Digest::MD5.hexdigest(data_string.to_der).scan(/../).join(':')
44
+ fingerprint_sha1 = OpenSSL::Digest::SHA1.hexdigest(data_string.to_der).scan(/../).join(':')
45
+ fingerprint_sha256 = OpenSSL::Digest::SHA256.hexdigest(data_string.to_der).scan(/../).join(':')
46
+
47
+ result[index]['fingerprints'] = {
48
+ "md5" => fingerprint_md5,
49
+ "sha1" => fingerprint_sha1,
50
+ "sha256" => fingerprint_sha256,
51
+ }
52
+
53
+ # If policy defined, then add compliance results
54
+ unless policy.nil?
55
+ policy_mgr = SSHScan::PolicyManager.new(result[index], policy)
56
+ result[index]['compliance'] = policy_mgr.compliance_results
57
+ end
29
58
  end
30
-
31
- data_string = OpenSSL::ASN1::Sequence([
32
- OpenSSL::ASN1::Integer.new(host_key.public_key.n),
33
- OpenSSL::ASN1::Integer.new(host_key.public_key.e)
34
- ])
35
-
36
- fingerprint_md5 = OpenSSL::Digest::MD5.hexdigest(data_string.to_der).scan(/../).join(':')
37
- fingerprint_sha1 = OpenSSL::Digest::SHA1.hexdigest(data_string.to_der).scan(/../).join(':')
38
- fingerprint_sha256 = OpenSSL::Digest::SHA256.hexdigest(data_string.to_der).scan(/../).join(':')
39
-
40
- result['fingerprints'] = {
41
- "md5" => fingerprint_md5,
42
- "sha1" => fingerprint_sha1,
43
- "sha256" => fingerprint_sha256,
44
- }
45
-
46
- # If policy defined, then add compliance results
47
- unless policy.nil?
48
- policy_mgr = SSHScan::PolicyManager.new(result, policy)
49
- result['compliance'] = policy_mgr.compliance_results
50
- end
51
-
52
59
  return result
53
60
  end
54
61
  end
@@ -1,12 +1,33 @@
1
1
  module SSHScan
2
2
  module SSHLib
3
3
  class OpenSSH
4
+ class Version
5
+ def initialize(version_string)
6
+ @version_string = version_string
7
+ end
8
+
9
+ def to_s
10
+ @version_string
11
+ end
12
+ end
13
+
14
+ def initialize(banner = nil)
15
+ @banner = banner
16
+ end
17
+
18
+ def version()
19
+ return nil if @banner.nil?
20
+ match = @banner.match(/OpenSSH_(\d+[\.\d+]+(p)?(\d+)?)/)
21
+ return nil if match.nil?
22
+ return OpenSSH::Version.new(match[1])
23
+ end
24
+
4
25
  def common
5
26
  "openssh"
6
27
  end
7
28
 
8
29
  def cpe
9
- "a:openssh:openssh"
30
+ "a:openssh:openssh" << (":" + version.to_s) unless version.nil?
10
31
  end
11
32
  end
12
33
  end
@@ -1,3 +1,3 @@
1
1
  module SSHScan
2
- VERSION = '0.0.8'
2
+ VERSION = '0.0.9.beta.1'
3
3
  end
data/ssh_scan.gemspec CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
28
28
 
29
29
  s.add_dependency('bindata', '~> 2.0')
30
30
  s.add_dependency('net-ssh')
31
+ s.add_dependency('netaddr')
31
32
  s.add_development_dependency('pry')
32
33
  s.add_development_dependency('rspec', '~> 3.0')
33
34
  s.add_development_dependency('rspec-its', '~> 1.2')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ssh_scan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9.beta.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Claudius
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-09 00:00:00.000000000 Z
11
+ date: 2016-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bindata
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: netaddr
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: pry
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -118,8 +132,10 @@ files:
118
132
  - lib/ssh_scan/os/centos.rb
119
133
  - lib/ssh_scan/os/debian.rb
120
134
  - lib/ssh_scan/os/freebsd.rb
135
+ - lib/ssh_scan/os/redhat.rb
121
136
  - lib/ssh_scan/os/ubuntu.rb
122
137
  - lib/ssh_scan/os/unknown.rb
138
+ - lib/ssh_scan/os/windows.rb
123
139
  - lib/ssh_scan/policy.rb
124
140
  - lib/ssh_scan/policy_manager.rb
125
141
  - lib/ssh_scan/protocol.rb
@@ -148,9 +164,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
148
164
  version: '0'
149
165
  required_rubygems_version: !ruby/object:Gem::Requirement
150
166
  requirements:
151
- - - ">="
167
+ - - ">"
152
168
  - !ruby/object:Gem::Version
153
- version: '0'
169
+ version: 1.3.1
154
170
  requirements: []
155
171
  rubyforge_project:
156
172
  rubygems_version: 2.6.2