ScanSSL 0.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1ade7f0a27b608559183de6a444401019558d66c
4
+ data.tar.gz: 15e1e854ba06cdc47589865f88c0b4ce7afef132
5
+ SHA512:
6
+ metadata.gz: 07dec5c21cf71cee698a7fe3831d7c57ee4f1e232b220c1fc57df55b9ca5e39204068569cbb52ac77285e6ef7adb9c91343e55d6cd29c5b5d4cd5fc9c9769c82
7
+ data.tar.gz: 445d6b2ad93032f71c1e1a15789e46ae93c7747547dd9435ec9416dd228fe728decf76c7b6d43ff3590fff98438e13149f6da6841d15863fc3a6cc401f0214cf
data/bin/scanssl ADDED
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # ScanSSL gem.
4
+ # Some info here.
5
+ #
6
+
7
+ require 'optparse'
8
+
9
+ unless File.respond_to? :realpath
10
+ class File #:nodoc:
11
+ def self.realpath path
12
+ return realpath(File.readlink(path)) if symlink?(path)
13
+ path
14
+ end
15
+ end
16
+ end
17
+ $: << File.expand_path(File.dirname(File.realpath(__FILE__)) + '/../lib')
18
+
19
+ require 'scanssl'
20
+ require 'rubygems'
21
+
22
+ options = {}
23
+
24
+ begin
25
+ OptionParser.new do |opts|
26
+ opts.banner = "Usage: #{File.basename($0)} [options]"
27
+
28
+ opts.on('-s', '--server server', 'Server to scan') do |server|
29
+ options[:server] = server
30
+ end
31
+
32
+ opts.on('-p', '--port port', 'Port to scan') do |port|
33
+ options[:port] = port
34
+ end
35
+
36
+ opts.on('-d', '--debug', 'Debug mode') do
37
+ options[:debug] = true
38
+ end
39
+
40
+ opts.on('-c', '--certificate', 'Displays certificate information') do
41
+ options[:check_cert] = true
42
+ ScanSSL
43
+ end
44
+
45
+ opts.on('-o', '--output filename', 'File to save results in') do |filename|
46
+ options[:output] = filename
47
+ end
48
+
49
+ opts.on('-t', '--type filetype', 'Type file: txt, pdf, html') do |filetype|
50
+ options[:file_type] = filetype
51
+ end
52
+
53
+ opts.on('-v', '--version', 'Version number') do
54
+ puts "ScanSSL version: #{ScanSSL::VERSION}"
55
+ exit
56
+ end
57
+
58
+ opts.on('-h', '--help', 'Displays Help') do
59
+ puts opts
60
+ exit
61
+ end
62
+ end.parse!
63
+
64
+ rescue OptionParser::MissingArgument
65
+ puts "Wrong argument. Check: -h for more help."
66
+
67
+ rescue OptionParser::InvalidOption
68
+ puts "Invalid argument. Check: -h for more help."
69
+ end
70
+
71
+ unless options[:server]
72
+ puts "Missing -s/--server argument!"
73
+ puts "Try -h"
74
+ exit
75
+ end
76
+
77
+ unless options[:port]
78
+ puts "Missing -p/--port argument!"
79
+ puts "Try -h"
80
+ exit
81
+ end
82
+
83
+ pid = fork do
84
+ ScanSSL::Command.call(options)
85
+ end
86
+
87
+ Signal.trap("INT") do
88
+ puts "Terminating Scan..."
89
+ Process.kill("TERM", pid)
90
+ exit 0
91
+ end
92
+
93
+ Process.waitpid(pid, 0)
data/lib/scanssl.rb ADDED
@@ -0,0 +1,58 @@
1
+ require 'colorize'
2
+ require 'getoptlong'
3
+ require 'openssl'
4
+ require 'socket'
5
+ require 'webrick'
6
+ require 'prawn'
7
+
8
+ require 'scanssl/version'
9
+ require 'scanssl/settings'
10
+ require 'scanssl/certInfo'
11
+ require 'scanssl/fileExport'
12
+ require 'scanssl/scanHost'
13
+
14
+ module ScanSSL
15
+ class Command
16
+ def self.call(options = {})
17
+ @server = options[:server]
18
+ @port = options[:port]
19
+
20
+ # Get Certiticate Information
21
+ if options[:check_cert] == true
22
+ a = ScanSSL::CertInfo.new(@server, @port)
23
+ # Not sure yet if we need to have an access to one of those
24
+ # datas so right now I'm returning each of them.
25
+ # We can convert it to hash or array.
26
+ colorOutputCert(a.valid?,
27
+ a.valid_from,
28
+ a.valid_until,
29
+ a.issuer,
30
+ a.subject,
31
+ a.algorithm,
32
+ a.key_size,
33
+ a.public_key)
34
+ end
35
+
36
+ if options[:check_cert] == nil
37
+ run = ScanSSL::ScanHost.new
38
+ puts run.scan(@server, @port)
39
+ end
40
+ end
41
+
42
+ def self.colorOutputCert(cValid, cFrom, cUntil, cIssuer, cSubject, cAlgorithm, cKey, cPublic)
43
+ puts "== Certificate Information ==".bold
44
+ puts "domain: #{@server}"
45
+ puts "port: #{@port}"
46
+ puts "----------------"
47
+ puts "valid: #{cValid}"
48
+ puts "valid from:#{cFrom}"
49
+ puts "valid until: #{cUntil}"
50
+ puts "issuer: #{cIssuer}"
51
+ puts "subject: #{cSubject}"
52
+ puts "algorithm: #{cAlgorithm}"
53
+ puts "key size: #{cKey}"
54
+ puts "public key:"
55
+ puts "#{cPublic}"
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,65 @@
1
+ module ScanSSL
2
+ class CertInfo < Certificate
3
+ def initialize(server, port)
4
+ @ssl_context = OpenSSL::SSL::SSLContext.new
5
+ @cert_store = OpenSSL::X509::Store.new
6
+ @cert_store.set_default_paths
7
+ @ssl_context.cert_store = @cert_store
8
+ @tcp_socket = TCPSocket.new(server, port)
9
+ @socket_destination = OpenSSL::SSL::SSLSocket.new @tcp_socket, @ssl_context
10
+ @socket_destination.connect
11
+ end
12
+
13
+ def valid?
14
+ return TRUTH_TABLE[(@socket_destination.verify_result == 0)]
15
+ end
16
+
17
+ def valid_from
18
+ return cert.not_before
19
+ end
20
+
21
+ def valid_until
22
+ return cert.not_after
23
+ end
24
+
25
+ def issuer
26
+ return certprops.select { |name, data, type| name == "O" }.first[1]
27
+ end
28
+
29
+ def subject
30
+ return cert.subject
31
+ end
32
+
33
+ def algorithm
34
+ return cert.signature_algorithm
35
+ end
36
+
37
+ def key_size
38
+ begin
39
+ key_size = OpenSSL::PKey::RSA.new(cert.public_key).to_text.match(/Public-Key: \((.*) bit/).to_a[1].strip.to_i
40
+ if key_size.between?(1000, 2000)
41
+ key_size = $1
42
+ elsif key_size > 2000
43
+ key_size = $1
44
+ else
45
+ key_size = $1
46
+ end
47
+ return key_size
48
+ end
49
+ rescue
50
+ return "Problem with key_size"
51
+ end
52
+
53
+ def public_key
54
+ return cert.public_key
55
+ end
56
+
57
+ def cert
58
+ return OpenSSL::X509::Certificate.new(@socket_destination.peer_cert)
59
+ end
60
+
61
+ def certprops
62
+ return OpenSSL::X509::Name.new(cert.issuer).to_a
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,18 @@
1
+ module ScanSSL
2
+ class Export
3
+ def self.pdf(file, data)
4
+ Prawn::Document.generate(file) do
5
+ text "Hello :-) This is my #{data}"
6
+ end
7
+ end
8
+
9
+ def self.txt(file, data)
10
+ ftxt = File.open("#{path}/#{file}", "a")
11
+ ftxt.write(data)
12
+ ftxt.close
13
+ end
14
+
15
+ def self.csv(file, data)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,128 @@
1
+ module ScanSSL
2
+ # I think the best will be to put the result to the hash or array
3
+ # and return it to ScanSLL::Command so we can send it to Colorize method
4
+ # and sort.
5
+ #
6
+ class ScanHost < Certificate
7
+ def scan(server, port)
8
+ ssl2_array = []
9
+ ssl3_array = []
10
+ tls1_array = []
11
+ tls1_1_array = []
12
+ tls1_2_array = []
13
+ threads = []
14
+
15
+ c = []
16
+ PROTOCOLS.each do |protocol|
17
+ ssl_context = OpenSSL::SSL::SSLContext.new
18
+ ssl_context.ciphers = CIPHERS
19
+ ssl_context.options = protocol
20
+ threads << Thread.new do
21
+ ssl_context.ciphers.each do |cipher|
22
+ begin
23
+ ssl_context = OpenSSL::SSL::SSLContext.new
24
+ ssl_context.options = protocol
25
+ ssl_context.ciphers = cipher[0].to_s
26
+ begin
27
+ tcp_socket = WEBrick::Utils.timeout(5){
28
+ TCPSocket.new(server, port)
29
+ }
30
+ rescue => e
31
+ puts e.message
32
+ exit 1
33
+ end
34
+ socket_destination = OpenSSL::SSL::SSLSocket.new tcp_socket, ssl_context
35
+ WEBrick::Utils.timeout(5) {
36
+ socket_destination.connect
37
+ }
38
+ if protocol == SSLV3
39
+ ssl_version, cipher, bits, vulnerability = result_parse(cipher[0], cipher[3], protocol)
40
+ result = "Server supports: %-22s %-42s %-10s %s\n"%[ssl_version, cipher, bits, vulnerability]
41
+ ssl3_array << result
42
+ elsif protocol == TLSV1
43
+ ssl_version, cipher, bits, vulnerability = result_parse(cipher[0], cipher[2], protocol)
44
+ result = "Server supports: %-22s %-42s %-10s %s\n"%[ssl_version, cipher, bits, vulnerability]
45
+ tls1_array << result
46
+ elsif protocol == TLSV1_1
47
+ ssl_version, cipher, bits, vulnerability = result_parse(cipher[0], cipher[2], protocol)
48
+ result = "Server supports: %-22s %-42s %-10s %s\n"%[ssl_version, cipher, bits, vulnerability]
49
+ tls1_1_array << result
50
+ elsif protocol == TLSV1_2
51
+ ssl_version, cipher, bits, vulnerability = result_parse(cipher[0], cipher[2], protocol)
52
+ result = "Server supports: %-22s %-42s %-10s %s\n"%[ssl_version, cipher, bits, vulnerability]
53
+ tls1_2_array << result
54
+ elsif protocol == SSLV2
55
+ ssl_version, cipher, bits, vulnerability = result_parse(cipher[0], cipher[2], protocol)
56
+ result = "Server supports: %-22s %-42s %-10s %s\n"%[ssl_version, cipher, bits, vulnerability]
57
+ ssl2_array << result
58
+ end
59
+
60
+ rescue Exception => e
61
+ if @debug
62
+ puts e.message
63
+ puts e.backtrace.join "\n"
64
+ if protocol == SSLV2
65
+ puts "Server Don't Supports: SSLv2 #{c[0]} #{c[2]} bits"
66
+ elsif protocol == SSLV3
67
+ puts "Server Don't Supports: SSLv3 #{c[0]} #{c[3]} bits"
68
+ elsif protocol == TLSV1
69
+ puts "Server Don't Supports: TLSv1 #{c[0]} #{c[2]} bits"
70
+ elsif protocol == TLSV1_1
71
+ puts "Server Don't Supports: TLSv1.1 #{c[0]} #{c[2]} bits"
72
+ elsif protocol == TLSV1_2
73
+ puts "Server Don't Supports: TLSv1.2 #{c[0]} #{c[2]} bits"
74
+ end
75
+ end
76
+ ensure
77
+ socket_destination.close if socket_destination rescue nil
78
+ tcp_socket.close if tcp_socket rescue nil
79
+ end
80
+ end
81
+ end
82
+ end
83
+ begin
84
+ threads.map(&:join)
85
+ rescue Interrupt
86
+ end
87
+ return ssl3_array, ssl2_array, tls1_array, tls1_1_array, tls1_2_array
88
+ end
89
+
90
+ def result_parse(cipher_name, cipher_bits, protocol)
91
+ ssl_version = PROTOCOL_COLOR_NAME[protocol]
92
+ cipher = case cipher_name
93
+ when /^(RC4|MD5)/
94
+ cipher_name.colorize(:yellow)
95
+ when /^RC2/
96
+ cipher_name.colorize(:red)
97
+ when /^EXP/
98
+ cipher_name.colorize(:red)
99
+ else
100
+ cipher_name.colorize(:gree)
101
+ end
102
+
103
+ bits = case cipher_bits
104
+ when 48, 56, 40
105
+ cipher_bits.to_s.colorize(:red)
106
+ when 112
107
+ cipher_bits.to_s.colorize(:yellow)
108
+ else
109
+ cipher_bits.to_s.colorize(:green)
110
+ end
111
+ detect_vulnerabilites(ssl_version, cipher, bits)
112
+ end
113
+
114
+ def detect_vulnerabilites(ssl_version, cipher, bits)
115
+ if ssl_version.match(/SSLv3/).to_s != "" && cipher.match(/RC/i).to_s == ""
116
+ return ssl_version, cipher, bits, " POODLE (CVE-2014-3566)".colorize(:red)
117
+ elsif cipher.match(/RC2/i)
118
+ return ssl_version, cipher, bits, " Chosen-Plaintext Attack".colorize(:red)
119
+ elsif cipher.match(/EXP/i)
120
+ return ssl_version, cipher, bits, " FREAK (CVE-2015-0204)".colorize(:red)
121
+ elsif cipher.match(/RC4/i)
122
+ return ssl_version, cipher, bits, " Bar-Mitzvah Attack".colorize(:yellow)
123
+ else
124
+ return ssl_version, cipher, bits, ''
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,28 @@
1
+ module ScanSSL
2
+ class Certificate
3
+ NO_SSLV2 = 16777216
4
+ NO_SSLV3 = 33554432
5
+ NO_TLSV1 = 67108864
6
+ NO_TLSV1_1 = 268435456
7
+ NO_TLSV1_2 = 134217728
8
+
9
+ SSLV2 = NO_SSLV3 + NO_TLSV1 + NO_TLSV1_1 + NO_TLSV1_2
10
+ SSLV3 = NO_SSLV2 + NO_TLSV1 + NO_TLSV1_1 + NO_TLSV1_2
11
+ TLSV1 = NO_SSLV2 + NO_SSLV3 + NO_TLSV1_1 + NO_TLSV1_2
12
+ TLSV1_1 = NO_SSLV2 + NO_SSLV3 + NO_TLSV1 + NO_TLSV1_2
13
+ TLSV1_2 = NO_SSLV2 + NO_SSLV3 + NO_TLSV1 + NO_TLSV1_1
14
+
15
+ PROTOCOLS = [SSLV2, SSLV3, TLSV1, TLSV1_1, TLSV1_2]
16
+ CIPHERS = 'ALL::COMPLEMENTOFDEFAULT::COMPLEMENTOFALL'
17
+
18
+ PROTOCOL_COLOR_NAME = {
19
+ SSLV2 => 'SSLv2',
20
+ SSLV3 => 'SSLv3',
21
+ TLSV1 => 'TLSv1',
22
+ TLSV1_1 => 'TLSv1.1',
23
+ TLSV1_2 => 'TLSv1.2'
24
+ }
25
+
26
+ TRUTH_TABLE = { true => 'true', false => 'false' }
27
+ end
28
+ end
@@ -0,0 +1,3 @@
1
+ module ScanSSL
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ScanSSL
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - bararchy
8
+ - ik5
9
+ - elichai
10
+ - Dor Lerner
11
+ - wolfedale
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+ date: 2015-11-03 00:00:00.000000000 Z
16
+ dependencies: []
17
+ description: A simple and easy to use SSL Cipher scanner
18
+ email: bar.hofesh@gmail.com
19
+ executables:
20
+ - scanssl
21
+ extensions: []
22
+ extra_rdoc_files: []
23
+ files:
24
+ - bin/scanssl
25
+ - lib/scanssl.rb
26
+ - lib/scanssl/certInfo.rb
27
+ - lib/scanssl/fileExport.rb
28
+ - lib/scanssl/scanHost.rb
29
+ - lib/scanssl/settings.rb
30
+ - lib/scanssl/version.rb
31
+ homepage: https://github.com/bararchy/ruby-SSLscanner
32
+ licenses:
33
+ - GPL3
34
+ metadata: {}
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 2.5.1
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: ScanSSL
55
+ test_files: []
56
+ has_rdoc: