ssh_scan 0.0.8 → 0.0.9.beta.1

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