ScanSSL 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/scanssl +93 -0
- data/lib/scanssl.rb +58 -0
- data/lib/scanssl/certInfo.rb +65 -0
- data/lib/scanssl/fileExport.rb +18 -0
- data/lib/scanssl/scanHost.rb +128 -0
- data/lib/scanssl/settings.rb +28 -0
- data/lib/scanssl/version.rb +3 -0
- metadata +56 -0
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
|
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:
|