ScanSSL 0.0.1

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