Browse Source

SSH support

master
Aeris 3 years ago
parent
commit
6de0fd5516
5 changed files with 278 additions and 0 deletions
  1. 1
    0
      bin/check_ssh
  2. 17
    0
      bin/check_ssh.rb
  3. 1
    0
      cryptcheck.gemspec
  4. 142
    0
      lib/cryptcheck/ssh/grade.rb
  5. 117
    0
      lib/cryptcheck/ssh/server.rb

+ 1
- 0
bin/check_ssh View File

@@ -0,0 +1 @@
1
+runner

+ 17
- 0
bin/check_ssh.rb View File

@@ -0,0 +1,17 @@
1
+#!/usr/bin/env ruby
2
+$:.unshift File.expand_path File.join File.dirname(__FILE__), '../lib'
3
+require 'rubygems'
4
+require 'bundler/setup'
5
+require 'cryptcheck'
6
+
7
+name, port, level = case ARGV.length
8
+	when 1 then [ARGV[0], 22, :info]
9
+	when 2 then [ARGV[0], ARGV[1], :info]
10
+	when 3 then [ARGV[0], ARGV[1], ARGV[3]]
11
+end
12
+
13
+::CryptCheck::Logger.level = level
14
+server = ::CryptCheck::Ssh::Server.new name, port
15
+#grade = ::CryptCheck::Ssh::Grade.new server
16
+#::CryptCheck::Logger.info { '' }
17
+#grade.display

+ 1
- 0
cryptcheck.gemspec View File

@@ -35,4 +35,5 @@ Gem::Specification.new do |spec|
35 35
 	spec.add_dependency 'parallel', '~> 1.3', '>= 1.3.4'
36 36
 	spec.add_dependency 'ruby-progressbar', '~> 1.7', '>= 1.7.1'
37 37
 	spec.add_dependency 'colorize', '~> 0.7', '>= 0.7.7'
38
+	spec.add_dependency 'net-ssh', '~> 2.9', '>= 2.9.2'
38 39
 end

+ 142
- 0
lib/cryptcheck/ssh/grade.rb View File

@@ -0,0 +1,142 @@
1
+module CryptCheck
2
+	module Tls
3
+		class TlsNotSupportedGrade
4
+			attr_reader :server, :score, :grade
5
+
6
+			def initialize(server)
7
+				@server, @score, @grade = server, -1, 'X'
8
+			end
9
+		end
10
+
11
+		class Grade
12
+			attr_reader :server, :protocol_score, :key_exchange_score, :cipher_strengths_score, :score, :grade, :error, :warning, :success
13
+
14
+			def initialize(server)
15
+				@server = server
16
+				calculate_protocol_score
17
+				calculate_key_exchange_score
18
+				calculate_cipher_strengths_score
19
+				@score = @protocol_score*0.3 + @key_exchange_score*0.3 + @cipher_strengths_score*0.4
20
+				calculate_error
21
+				calculate_warning
22
+				calculate_success
23
+				calculate_grade
24
+			end
25
+
26
+			def display
27
+				color = case self.grade
28
+							when 'A+' then :blue
29
+							when 'A' then :green
30
+							when 'B', 'C' then :yellow
31
+							when 'E', 'F' then :red
32
+							when 'M', 'T' then { color: :white, background: :red }
33
+						end
34
+
35
+				Logger.info { "Grade : #{self.grade.colorize color }" }
36
+				Logger.info { '' }
37
+				Logger.info { "Protocole : #{self.protocol_score} / 100" }
38
+				Logger.info { "Key exchange : #{self.key_exchange_score} / 100" }
39
+				Logger.info { "Ciphers strength : #{self.cipher_strengths_score} / 100" }
40
+				Logger.info { "Overall score : #{self.score} / 100" }
41
+				Logger.info { '' }
42
+				Logger.info { "Errors : #{self.error.join(' ').colorize :red }" } unless self.error.empty?
43
+				Logger.info { "Warnings : #{self.warning.join(' ').colorize :yellow }" } unless self.warning.empty?
44
+				Logger.info { "Best practices : #{self.success.join(' ').colorize :green }" } unless self.success.empty?
45
+			end
46
+
47
+			private
48
+			def calculate_grade
49
+				@grade = case @score
50
+							 when 0...20 then 'F'
51
+							 when 20...35 then 'E'
52
+							 when 35...50 then 'D'
53
+							 when 50...65 then 'C'
54
+							 when 65...80 then 'B'
55
+							 else 'A'
56
+						 end
57
+
58
+				@grade = [@grade, 'B'].max if !@server.tlsv1_2? or @server.key_size < 2048
59
+				@grade = [@grade, 'C'].max if @server.des3?
60
+				@grade = [@grade, 'F'].max unless @error.empty?
61
+
62
+				@grade = 'M' unless @server.cert_valid
63
+				@grade = 'T' unless @server.cert_trusted
64
+
65
+				@grade = 'A+' if @grade == 'A' and @error.empty? and @warning.empty? and (all_success & @success) == all_success
66
+			end
67
+
68
+			def calculate_error
69
+				@error = []
70
+
71
+				@error << :md5_sig if @server.md5_sig?
72
+				@error << :sslv2 if @server.sslv2?
73
+				@error << :sslv3 if @server.sslv3?
74
+
75
+				@error << :md5 if @server.md5?
76
+				@error << :anonymous if @server.anonymous?
77
+				@error << :dss if @server.dss?
78
+
79
+				@error << :null if @server.null?
80
+				@error << :export if @server.export?
81
+				@error << :des if @server.des?
82
+				@error << :rc4 if @server.rc4?
83
+			end
84
+
85
+			def calculate_warning
86
+				@warning = []
87
+
88
+				@warning << :sha1_sig if @server.sha1_sig?
89
+
90
+				#@warning << :sha1 if @server.sha1?
91
+
92
+				@warning << :des3 if @server.des3?
93
+			end
94
+
95
+			def calculate_success
96
+				@success = []
97
+				@success << :pfs if @server.pfs_only?
98
+			end
99
+
100
+			ALL_ERROR = %i(md5_sig md5 anonymous dss null export des rc4)
101
+			def all_error
102
+				ALL_ERROR
103
+			end
104
+
105
+			ALL_WARNING = %i(sha1_sig des3)
106
+			def all_warning
107
+				ALL_WARNING
108
+			end
109
+
110
+			ALL_SUCCESS = %i(pfs)
111
+			def all_success
112
+				ALL_SUCCESS
113
+			end
114
+
115
+			METHODS_SCORES = { SSLv2: 0, SSLv3: 20, TLSv1: 60, TLSv1_1: 80, TLSv1_2: 100 }
116
+			def calculate_protocol_score
117
+				@protocol_score = @server.supported_protocols.collect { |p| METHODS_SCORES[p] }.min
118
+			end
119
+
120
+			def calculate_key_exchange_score
121
+				@key_exchange_score = case @server.key_size
122
+										  when 0 then 0
123
+										  when 0...512 then 10
124
+										  when 512...1024 then 20
125
+										  when 1024...2048 then 50
126
+										  when 2048...4096 then 90
127
+										  else 100
128
+									  end
129
+			end
130
+
131
+			def calculate_cipher_strengths_score
132
+				@cipher_strengths_score = case @server.cipher_size
133
+					when 0 then 0
134
+					when 0...112 then 10
135
+					when 112...128 then 50
136
+					when 128...256 then 90
137
+					else 100
138
+				end
139
+			end
140
+		end
141
+	end
142
+end

+ 117
- 0
lib/cryptcheck/ssh/server.rb View File

@@ -0,0 +1,117 @@
1
+require 'net/ssh'
2
+
3
+module CryptCheck
4
+	module Ssh
5
+		class SshNotSupportedServer
6
+			attr_reader :hostname, :port
7
+
8
+			def initialize(hostname, port)
9
+				@hostname, @port = hostname, port
10
+			end
11
+		end
12
+
13
+		class Server
14
+			TCP_TIMEOUT       = 10
15
+			class SshNotAvailableException < Exception
16
+			end
17
+
18
+			attr_reader :hostname, :port, :kex, :encryption, :hmac, :compression, :key
19
+
20
+			KEX = {
21
+				'curve25519-sha256@libssh.org' => :green,
22
+				'diffie-hellman-group1-sha1' => :yellow,
23
+				'diffie-hellman-group14-sha1' => :yellow,
24
+				'diffie-hellman-group-exchange-sha1' => :yellow,
25
+				'diffie-hellman-group-exchange-sha256' => :green,
26
+				'ecdh-sha2-nistp256' => :yellow,
27
+				'ecdh-sha2-nistp384' => :yellow,
28
+				'ecdh-sha2-nistp521' => :yellow
29
+			}
30
+
31
+			ENCRYPTION = {
32
+				'3des-cbc' => :red,
33
+				'aes128-cbc' => :yellow,
34
+				'aes192-cbc' => :yellow,
35
+				'aes256-cbc' => :yellow,
36
+				'aes128-ctr' => :yellow,
37
+				'aes192-ctr' => :yellow,
38
+				'aes256-ctr' => :yellow,
39
+				'aes128-gcm@openssh.com' => :green,
40
+				'aes256-gcm@openssh.com' => :green,
41
+				'arcfour' => :red,
42
+				'arcfour128' => :red,
43
+				'arcfour256' => :red,
44
+				'blowfish-cbc' => :yellow,
45
+				'cast128-cbc' => nil,
46
+				'chacha20-poly1305@openssh.com' => :green
47
+			}
48
+
49
+			HMAC = {
50
+				'hmac-md5' => :red,
51
+				'hmac-md5-96' => :red,
52
+				'hmac-ripemd160' => :green,
53
+				'hmac-sha1' => :yellow,
54
+				'hmac-sha1-96' => :red,
55
+				'hmac-sha2-256' => :green,
56
+				'hmac-sha2-512' => :green,
57
+				'umac-64@openssh.com' => :red,
58
+				'umac-128@openssh.com' => nil,
59
+				'hmac-md5-etm@openssh.com' => :red,
60
+				'hmac-md5-96-etm@openssh.com' => :red,
61
+				'hmac-ripemd160-etm@openssh.com' => :green,
62
+				'hmac-sha1-etm@openssh.com' => :yellow,
63
+				'hmac-sha1-96-etm@openssh.com' => :red,
64
+				'hmac-sha2-256-etm@openssh.com' => :green,
65
+				'hmac-sha2-512-etm@openssh.com' => :green,
66
+				'umac-64-etm@openssh.com' => :red,
67
+				'umac-128-etm@openssh.com' => nil
68
+			}
69
+
70
+			COMPRESSION = {
71
+				'none' => nil,
72
+				'zlib@openssh.com' => nil
73
+			}
74
+
75
+			KEY = {
76
+				'ecdsa-sha2-nistp256-cert-v01@openssh.com' => :yellow,
77
+				'ecdsa-sha2-nistp384-cert-v01@openssh.com' => :yellow,
78
+				'ecdsa-sha2-nistp521-cert-v01@openssh.com' => :yellow,
79
+				'ssh-ed25519-cert-v01@openssh.com' => :green,
80
+				'ssh-rsa-cert-v01@openssh.com' => :yellow,
81
+				'ssh-dss-cert-v01@openssh.com' => :red,
82
+				'ssh-rsa-cert-v00@openssh.com' => :yellow,
83
+				'ssh-dss-cert-v00@openssh.com' => :red,
84
+				'ecdsa-sha2-nistp256' => :yellow,
85
+				'ecdsa-sha2-nistp384' => :yellow,
86
+				'ecdsa-sha2-nistp521' => :yellow,
87
+				'ssh-ed25519' => :green,
88
+				'ssh-rsa' => :yellow,
89
+				'ssh-dss' => :red
90
+			}
91
+
92
+			def initialize(hostname, port)
93
+				@hostname, @port = hostname, port
94
+
95
+				Logger.info { "#{hostname}:#{port}".colorize :blue }
96
+				session = ::Net::SSH::Transport::Session.new @hostname, port: @port, timeout: TCP_TIMEOUT
97
+				data = session.algorithms.instance_variable_get :@server_data
98
+				@kex, @encryption, @hmac, @compression, @key = data[:kex], data[:encryption_server], data[:hmac_server], data[:compression_server], data[:host_key]
99
+
100
+				Logger.info { '' }
101
+				@kex.each { |k| Logger.info { "Key exchange : #{k.colorize KEX[k]}" } }
102
+				Logger.info { '' }
103
+				@encryption.each { |e| Logger.info { "Encryption : #{e.colorize ENCRYPTION[e]}" } }
104
+				Logger.info { '' }
105
+				@hmac.each { |h| Logger.info { "HMAC : #{h.colorize HMAC[h]}" } }
106
+				Logger.info { '' }
107
+				@compression.each { |c| Logger.info { "Compression : #{c}" } }
108
+				Logger.info { '' }
109
+				@key.each { |k| Logger.info { "Key type : #{k.colorize KEY[k]}" } }
110
+				session.close
111
+			rescue => e
112
+				Logger.debug { "SSH not supported : #{e}" }
113
+				raise SshNotAvailableException, e
114
+			end
115
+		end
116
+	end
117
+end

Loading…
Cancel
Save