rex-sslscan 0.1.0
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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +5 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/README.md +40 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rex/sslscan.rb +8 -0
- data/lib/rex/sslscan/result.rb +221 -0
- data/lib/rex/sslscan/scanner.rb +206 -0
- data/lib/rex/sslscan/version.rb +5 -0
- data/rex-sslscan.gemspec +29 -0
- metadata +207 -0
- metadata.gz.sig +3 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7bba5040d04f2a50358536c9a8eeb17866ec1099
|
4
|
+
data.tar.gz: 25c7443cafa1c03b69678288ac78b9c88706bb34
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: afa09af1ced88401559351e1dbcf4b4af2e396479a82d29278d943b764c440869e789f77a48a7f91dc28e0343c9c199a8bb277c7781c2ec1894be1ae53b3681e
|
7
|
+
data.tar.gz: 2006419b7577f701602bca96ee16b705d62cf8262dbcb004bf6a876ecdef1aac1ac72588930cfd18980da775b941407672fdc14caee80f56dc85b8dcd4f1f1c6
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at DMaloney@rapid7.com. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Rex::SSLScan
|
2
|
+
|
3
|
+
This library is a pure ruby implmenetation of the SSLScan tool originally written by Ian Ventura-Whiting. It currently depends on the system version of OpenSSL.
|
4
|
+
To be effective you will want to use this gem with a version of OpenSSL compiled to support the old versions of SSL and weak ciphers. We will be ivnestigating ways
|
5
|
+
to either prepackage a version of OpenSSL with this gem, or using an alternative in the future.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'rex-sslscan'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install rex-sslscan
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
```
|
26
|
+
scanner = Rex::SSLScan::Scanner.new('192.168.1.1', 443)
|
27
|
+
results = scanner.scan
|
28
|
+
print_status results.to_s
|
29
|
+
```
|
30
|
+
|
31
|
+
## Development
|
32
|
+
|
33
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
34
|
+
|
35
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
36
|
+
|
37
|
+
## Contributing
|
38
|
+
|
39
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/rex-sslscan. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
40
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rex/sslscan"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/lib/rex/sslscan.rb
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
|
3
|
+
require 'rex/socket'
|
4
|
+
require 'rex/text/table'
|
5
|
+
require 'set'
|
6
|
+
|
7
|
+
module Rex::SSLScan
|
8
|
+
class Result
|
9
|
+
|
10
|
+
attr_accessor :openssl_sslv2
|
11
|
+
|
12
|
+
attr_reader :ciphers
|
13
|
+
attr_reader :supported_versions
|
14
|
+
|
15
|
+
def initialize()
|
16
|
+
@cert = nil
|
17
|
+
@ciphers = Set.new
|
18
|
+
@supported_versions = [:SSLv2, :SSLv3, :TLSv1]
|
19
|
+
@deprecated_weak_ciphers = [
|
20
|
+
'ECDHE-RSA-DES-CBC3-SHA',
|
21
|
+
'ECDHE-ECDSA-DES-CBC3-SHA',
|
22
|
+
'SRP-DSS-3DES-EDE-CBC-SHA',
|
23
|
+
'SRP-RSA-3DES-EDE-CBC-SHA',
|
24
|
+
'SRP-3DES-EDE-CBC-SHA',
|
25
|
+
'EDH-RSA-DES-CBC3-SHA',
|
26
|
+
'EDH-DSS-DES-CBC3-SHA',
|
27
|
+
'ECDH-RSA-DES-CBC3-SHA',
|
28
|
+
'ECDH-ECDSA-DES-CBC3-SHA',
|
29
|
+
'DES-CBC3-SHA',
|
30
|
+
'PSK-3DES-EDE-CBC-SHA',
|
31
|
+
'EXP-EDH-RSA-DES-CBC-SHA',
|
32
|
+
'EXP-EDH-DSS-DES-CBC-SHA',
|
33
|
+
'EXP-DES-CBC-SHA',
|
34
|
+
'EXP-RC2-CBC-MD5',
|
35
|
+
'EXP-RC4-MD5'
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
def cert
|
40
|
+
@cert
|
41
|
+
end
|
42
|
+
|
43
|
+
def cert=(input)
|
44
|
+
unless input.kind_of? OpenSSL::X509::Certificate or input.nil?
|
45
|
+
raise ArgumentError, "Must be an X509 Cert!"
|
46
|
+
end
|
47
|
+
@cert = input
|
48
|
+
end
|
49
|
+
|
50
|
+
def sslv2
|
51
|
+
@ciphers.reject{|cipher| cipher[:version] != :SSLv2 }
|
52
|
+
end
|
53
|
+
|
54
|
+
def sslv3
|
55
|
+
@ciphers.reject{|cipher| cipher[:version] != :SSLv3 }
|
56
|
+
end
|
57
|
+
|
58
|
+
def tlsv1
|
59
|
+
@ciphers.reject{|cipher| cipher[:version] != :TLSv1 }
|
60
|
+
end
|
61
|
+
|
62
|
+
def weak_ciphers
|
63
|
+
accepted.reject{|cipher| cipher[:weak] == false }
|
64
|
+
end
|
65
|
+
|
66
|
+
def strong_ciphers
|
67
|
+
accepted.reject{|cipher| cipher[:weak] }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns all accepted ciphers matching the supplied version
|
71
|
+
# @param version [Symbol, Array] The SSL Version to filter on
|
72
|
+
# @raise [ArgumentError] if the version supplied is invalid
|
73
|
+
# @return [Array] An array of accepted cipher details matching the supplied versions
|
74
|
+
def accepted(version = :all)
|
75
|
+
enum_ciphers(:accepted, version)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns all rejected ciphers matching the supplied version
|
79
|
+
# @param version [Symbol, Array] The SSL Version to filter on
|
80
|
+
# @raise [ArgumentError] if the version supplied is invalid
|
81
|
+
# @return [Array] An array of rejected cipher details matching the supplied versions
|
82
|
+
def rejected(version = :all)
|
83
|
+
enum_ciphers(:rejected, version)
|
84
|
+
end
|
85
|
+
|
86
|
+
def each_accepted(version = :all)
|
87
|
+
accepted(version).each do |cipher_result|
|
88
|
+
yield cipher_result
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def each_rejected(version = :all)
|
93
|
+
rejected(version).each do |cipher_result|
|
94
|
+
yield cipher_result
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def supports_sslv2?
|
99
|
+
!(accepted(:SSLv2).empty?)
|
100
|
+
end
|
101
|
+
|
102
|
+
def supports_sslv3?
|
103
|
+
!(accepted(:SSLv3).empty?)
|
104
|
+
end
|
105
|
+
|
106
|
+
def supports_tlsv1?
|
107
|
+
!(accepted(:TLSv1).empty?)
|
108
|
+
end
|
109
|
+
|
110
|
+
def supports_ssl?
|
111
|
+
supports_sslv2? or supports_sslv3? or supports_tlsv1?
|
112
|
+
end
|
113
|
+
|
114
|
+
def supports_weak_ciphers?
|
115
|
+
!(weak_ciphers.empty?)
|
116
|
+
end
|
117
|
+
|
118
|
+
def standards_compliant?
|
119
|
+
if supports_ssl?
|
120
|
+
return false if supports_sslv2?
|
121
|
+
return false if supports_weak_ciphers?
|
122
|
+
end
|
123
|
+
true
|
124
|
+
end
|
125
|
+
|
126
|
+
# Adds the details of a cipher test to the Result object.
|
127
|
+
# @param version [Symbol] the SSL Version
|
128
|
+
# @param cipher [String] the SSL cipher
|
129
|
+
# @param key_length [Fixnum] the length of encryption key
|
130
|
+
# @param status [Symbol] :accepted or :rejected
|
131
|
+
def add_cipher(version, cipher, key_length, status)
|
132
|
+
unless @supported_versions.include? version
|
133
|
+
raise ArgumentError, "Must be a supported SSL Version"
|
134
|
+
end
|
135
|
+
unless OpenSSL::SSL::SSLContext.new(version).ciphers.flatten.include?(cipher) \
|
136
|
+
|| @deprecated_weak_ciphers.include?(cipher)
|
137
|
+
raise ArgumentError, "Must be a valid SSL Cipher for #{version}!"
|
138
|
+
end
|
139
|
+
unless key_length.kind_of? Fixnum
|
140
|
+
raise ArgumentError, "Must supply a valid key length"
|
141
|
+
end
|
142
|
+
unless [:accepted, :rejected].include? status
|
143
|
+
raise ArgumentError, "Status must be either :accepted or :rejected"
|
144
|
+
end
|
145
|
+
|
146
|
+
strong_cipher_ctx = OpenSSL::SSL::SSLContext.new(version)
|
147
|
+
# OpenSSL Directive For Strong Ciphers
|
148
|
+
# See: http://www.rapid7.com/vulndb/lookup/ssl-weak-ciphers
|
149
|
+
strong_cipher_ctx.ciphers = "ALL:!aNULL:!eNULL:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM"
|
150
|
+
|
151
|
+
if strong_cipher_ctx.ciphers.flatten.include? cipher
|
152
|
+
weak = false
|
153
|
+
else
|
154
|
+
weak = true
|
155
|
+
end
|
156
|
+
|
157
|
+
cipher_details = {:version => version, :cipher => cipher, :key_length => key_length, :weak => weak, :status => status}
|
158
|
+
@ciphers << cipher_details
|
159
|
+
end
|
160
|
+
|
161
|
+
def to_s
|
162
|
+
unless supports_ssl?
|
163
|
+
return "Server does not appear to support SSL on this port!"
|
164
|
+
end
|
165
|
+
table = Rex::Text::Table.new(
|
166
|
+
'Header' => 'SSL Ciphers',
|
167
|
+
'Indent' => 1,
|
168
|
+
'Columns' => ['Status', 'Weak', 'SSL Version', 'Key Length', 'Cipher'],
|
169
|
+
'SortIndex' => -1
|
170
|
+
)
|
171
|
+
ciphers.each do |cipher|
|
172
|
+
if cipher[:weak]
|
173
|
+
weak = '*'
|
174
|
+
else
|
175
|
+
weak = ' '
|
176
|
+
end
|
177
|
+
table << [cipher[:status].to_s.capitalize, weak , cipher[:version], cipher[:key_length], cipher[:cipher]]
|
178
|
+
end
|
179
|
+
|
180
|
+
# Sort by SSL Version, then Key Length, and then Status
|
181
|
+
table.rows.sort_by!{|row| [row[0],row[2],row[3]]}
|
182
|
+
text = "#{table.to_s}"
|
183
|
+
if @cert
|
184
|
+
text << " \n\n #{@cert.to_text}"
|
185
|
+
end
|
186
|
+
if openssl_sslv2 == false
|
187
|
+
text << "\n\n *** WARNING: Your OS hates freedom! Your OpenSSL libs are compiled without SSLv2 support!"
|
188
|
+
end
|
189
|
+
text
|
190
|
+
end
|
191
|
+
|
192
|
+
protected
|
193
|
+
|
194
|
+
# @param state [Symbol] Either :accepted or :rejected
|
195
|
+
# @param version [Symbol, Array] The SSL Version to filter on (:SSLv2, :SSLv3, :TLSv1, :all)
|
196
|
+
# @return [Set] The Set of cipher results matching the filter criteria
|
197
|
+
def enum_ciphers(state, version = :all)
|
198
|
+
case version
|
199
|
+
when Symbol
|
200
|
+
case version
|
201
|
+
when :all
|
202
|
+
return @ciphers.select{|cipher| cipher[:status] == state}
|
203
|
+
when :SSLv2, :SSLv3, :TLSv1
|
204
|
+
return @ciphers.select{|cipher| cipher[:status] == state and cipher[:version] == version}
|
205
|
+
else
|
206
|
+
raise ArgumentError, "Invalid SSL Version Supplied: #{version}"
|
207
|
+
end
|
208
|
+
when Array
|
209
|
+
version = version.reject{|v| !(@supported_versions.include? v)}
|
210
|
+
if version.empty?
|
211
|
+
return @ciphers.select{|cipher| cipher[:status] == state}
|
212
|
+
else
|
213
|
+
return @ciphers.select{|cipher| cipher[:status] == state and version.include? cipher[:version]}
|
214
|
+
end
|
215
|
+
else
|
216
|
+
raise ArgumentError, "Was expecting Symbol or Array and got #{version.class}"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
# -*- coding: binary -*-
|
2
|
+
require 'rex/socket'
|
3
|
+
require 'rex/sslscan/result'
|
4
|
+
|
5
|
+
module Rex::SSLScan
|
6
|
+
|
7
|
+
class Scanner
|
8
|
+
|
9
|
+
attr_accessor :context
|
10
|
+
attr_accessor :host
|
11
|
+
attr_accessor :port
|
12
|
+
attr_accessor :timeout
|
13
|
+
|
14
|
+
attr_reader :supported_versions
|
15
|
+
attr_reader :sslv2
|
16
|
+
|
17
|
+
# Initializes the scanner object
|
18
|
+
# @param host [String] IP address or hostname to scan
|
19
|
+
# @param port [Fixnum] Port number to scan, default: 443
|
20
|
+
# @param timeout [Fixnum] Timeout for connections, in seconds. default: 5
|
21
|
+
# @raise [StandardError] Raised when the configuration is invalid
|
22
|
+
def initialize(host,port = 443,context = {},timeout=5)
|
23
|
+
@host = host
|
24
|
+
@port = port
|
25
|
+
@timeout = timeout
|
26
|
+
@context = context
|
27
|
+
if check_opensslv2 == true
|
28
|
+
@supported_versions = [:SSLv2, :SSLv3, :TLSv1]
|
29
|
+
@sslv2 = true
|
30
|
+
else
|
31
|
+
@supported_versions = [:SSLv3, :TLSv1]
|
32
|
+
@sslv2 = false
|
33
|
+
end
|
34
|
+
raise StandardError, "The scanner configuration is invalid" unless valid?
|
35
|
+
end
|
36
|
+
|
37
|
+
# Checks whether the scanner option has a valid configuration
|
38
|
+
# @return [Boolean] True or False, the configuration is valid.
|
39
|
+
def valid?
|
40
|
+
begin
|
41
|
+
@host = Rex::Socket.getaddress(@host, true)
|
42
|
+
rescue
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
return false unless @port.kind_of? Fixnum
|
46
|
+
return false unless @port >= 0 and @port <= 65535
|
47
|
+
return false unless @timeout.kind_of? Fixnum
|
48
|
+
return true
|
49
|
+
end
|
50
|
+
|
51
|
+
# Initiate the Scan against the target. Will test each cipher one at a time.
|
52
|
+
# @return [Result] object containing the details of the scan
|
53
|
+
def scan
|
54
|
+
scan_result = Rex::SSLScan::Result.new
|
55
|
+
scan_result.openssl_sslv2 = sslv2
|
56
|
+
# If we can't get any SSL connection, then don't bother testing
|
57
|
+
# individual ciphers.
|
58
|
+
if test_ssl == :rejected and test_tls == :rejected
|
59
|
+
return scan_result
|
60
|
+
end
|
61
|
+
|
62
|
+
@supported_versions.each do |ssl_version|
|
63
|
+
sslctx = OpenSSL::SSL::SSLContext.new(ssl_version)
|
64
|
+
sslctx.ciphers.each do |cipher_name, ssl_ver, key_length, alg_length|
|
65
|
+
status = test_cipher(ssl_version, cipher_name)
|
66
|
+
scan_result.add_cipher(ssl_version, cipher_name, key_length, status)
|
67
|
+
if status == :accepted and scan_result.cert.nil?
|
68
|
+
scan_result.cert = get_cert(ssl_version, cipher_name)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
scan_result
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_ssl
|
76
|
+
begin
|
77
|
+
scan_client = Rex::Socket::Tcp.create(
|
78
|
+
'Context' => @context,
|
79
|
+
'PeerHost' => @host,
|
80
|
+
'PeerPort' => @port,
|
81
|
+
'SSL' => true,
|
82
|
+
'SSLVersion' => :SSLv23,
|
83
|
+
'Timeout' => @timeout
|
84
|
+
)
|
85
|
+
rescue ::Exception => e
|
86
|
+
return :rejected
|
87
|
+
ensure
|
88
|
+
if scan_client
|
89
|
+
scan_client.close
|
90
|
+
end
|
91
|
+
end
|
92
|
+
return :accepted
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_tls
|
96
|
+
begin
|
97
|
+
scan_client = Rex::Socket::Tcp.create(
|
98
|
+
'Context' => @context,
|
99
|
+
'PeerHost' => @host,
|
100
|
+
'PeerPort' => @port,
|
101
|
+
'SSL' => true,
|
102
|
+
'SSLVersion' => :TLSv1,
|
103
|
+
'Timeout' => @timeout
|
104
|
+
)
|
105
|
+
rescue ::Exception => e
|
106
|
+
return :rejected
|
107
|
+
ensure
|
108
|
+
if scan_client
|
109
|
+
scan_client.close
|
110
|
+
end
|
111
|
+
end
|
112
|
+
return :accepted
|
113
|
+
end
|
114
|
+
|
115
|
+
# Tests the specified SSL Version and Cipher against the configured target
|
116
|
+
# @param ssl_version [Symbol] The SSL version to use (:SSLv2, :SSLv3, :TLSv1)
|
117
|
+
# @param cipher [String] The SSL Cipher to use
|
118
|
+
# @return [Symbol] Either :accepted or :rejected
|
119
|
+
def test_cipher(ssl_version, cipher)
|
120
|
+
validate_params(ssl_version,cipher)
|
121
|
+
begin
|
122
|
+
scan_client = Rex::Socket::Tcp.create(
|
123
|
+
'Context' => @context,
|
124
|
+
'PeerHost' => @host,
|
125
|
+
'PeerPort' => @port,
|
126
|
+
'SSL' => true,
|
127
|
+
'SSLVersion' => ssl_version,
|
128
|
+
'SSLCipher' => cipher,
|
129
|
+
'Timeout' => @timeout
|
130
|
+
)
|
131
|
+
rescue ::Exception => e
|
132
|
+
return :rejected
|
133
|
+
ensure
|
134
|
+
if scan_client
|
135
|
+
scan_client.close
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
return :accepted
|
140
|
+
end
|
141
|
+
|
142
|
+
# Retrieve the X509 Cert from the target service,
|
143
|
+
# @param ssl_version [Symbol] The SSL version to use (:SSLv2, :SSLv3, :TLSv1)
|
144
|
+
# @param cipher [String] The SSL Cipher to use
|
145
|
+
# @return [OpenSSL::X509::Certificate] if the certificate was retrieved
|
146
|
+
# @return [Nil] if the cert couldn't be retrieved
|
147
|
+
def get_cert(ssl_version, cipher)
|
148
|
+
validate_params(ssl_version,cipher)
|
149
|
+
begin
|
150
|
+
scan_client = Rex::Socket::Tcp.create(
|
151
|
+
'PeerHost' => @host,
|
152
|
+
'PeerPort' => @port,
|
153
|
+
'SSL' => true,
|
154
|
+
'SSLVersion' => ssl_version,
|
155
|
+
'SSLCipher' => cipher,
|
156
|
+
'Timeout' => @timeout
|
157
|
+
)
|
158
|
+
cert = scan_client.peer_cert
|
159
|
+
if cert.kind_of? OpenSSL::X509::Certificate
|
160
|
+
return cert
|
161
|
+
else
|
162
|
+
return nil
|
163
|
+
end
|
164
|
+
rescue ::Exception => e
|
165
|
+
return nil
|
166
|
+
ensure
|
167
|
+
if scan_client
|
168
|
+
scan_client.close
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
protected
|
175
|
+
|
176
|
+
# Validates that the SSL Version and Cipher are valid both seperately and
|
177
|
+
# together as part of an SSL Context.
|
178
|
+
# @param ssl_version [Symbol] The SSL version to use (:SSLv2, :SSLv3, :TLSv1)
|
179
|
+
# @param cipher [String] The SSL Cipher to use
|
180
|
+
# @raise [StandardError] If an invalid or unsupported SSL Version was supplied
|
181
|
+
# @raise [StandardError] If the cipher is not valid for that version of SSL
|
182
|
+
def validate_params(ssl_version, cipher)
|
183
|
+
raise StandardError, "The scanner configuration is invalid" unless valid?
|
184
|
+
unless @supported_versions.include? ssl_version
|
185
|
+
raise StandardError, "SSL Version must be one of: #{@supported_versions.to_s}"
|
186
|
+
end
|
187
|
+
if ssl_version == :SSLv2 and sslv2 == false
|
188
|
+
raise StandardError, "Your OS hates freedom! Your OpenSSL libs are compiled without SSLv2 support!"
|
189
|
+
else
|
190
|
+
unless OpenSSL::SSL::SSLContext.new(ssl_version).ciphers.flatten.include? cipher
|
191
|
+
raise StandardError, "Must be a valid SSL Cipher for #{ssl_version}!"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def check_opensslv2
|
197
|
+
begin
|
198
|
+
OpenSSL::SSL::SSLContext.new(:SSLv2)
|
199
|
+
rescue
|
200
|
+
return false
|
201
|
+
end
|
202
|
+
return true
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
end
|
data/rex-sslscan.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rex/sslscan/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "rex-sslscan"
|
8
|
+
spec.version = Rex::Sslscan::VERSION
|
9
|
+
spec.authors = ["David Maloney"]
|
10
|
+
spec.email = ["DMaloney@rapid7.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Ruby Exploitation(REX) Library for scanning the SSL/TLS capabilities of a server}
|
13
|
+
spec.description = %q{This library is a pure ruby implmenetation of the SSLScan tool originally written
|
14
|
+
by Ian Ventura-Whiting. It currently depends on the system version of OpenSSL}
|
15
|
+
spec.homepage = "https://github.com/rapid7/rex-sslscan"
|
16
|
+
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
26
|
+
|
27
|
+
spec.add_runtime_dependency "rex-text"
|
28
|
+
spec.add_runtime_dependency "rex-socket"
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rex-sslscan
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Maloney
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
|
14
|
+
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
|
15
|
+
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
|
16
|
+
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
|
17
|
+
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
|
18
|
+
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
|
19
|
+
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
|
20
|
+
xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
|
21
|
+
1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
|
22
|
+
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
|
23
|
+
U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
|
24
|
+
9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
|
25
|
+
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
|
26
|
+
AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
|
27
|
+
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
|
28
|
+
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
|
29
|
+
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
|
30
|
+
DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
|
31
|
+
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
|
32
|
+
-----END CERTIFICATE-----
|
33
|
+
- |
|
34
|
+
-----BEGIN CERTIFICATE-----
|
35
|
+
MIIEKDCCAxCgAwIBAgILBAAAAAABL07hNVwwDQYJKoZIhvcNAQEFBQAwVzELMAkG
|
36
|
+
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
|
37
|
+
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMTA0MTMxMDAw
|
38
|
+
MDBaFw0xOTA0MTMxMDAwMDBaMFExCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
|
39
|
+
YWxTaWduIG52LXNhMScwJQYDVQQDEx5HbG9iYWxTaWduIENvZGVTaWduaW5nIENB
|
40
|
+
IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCyTxTnEL7XJnKr
|
41
|
+
NpfvU79ChF5Y0Yoo/ENGb34oRFALdV0A1zwKRJ4gaqT3RUo3YKNuPxL6bfq2RsNq
|
42
|
+
o7gMJygCVyjRUPdhOVW4w+ElhlI8vwUd17Oa+JokMUnVoqni05GrPjxz7/Yp8cg1
|
43
|
+
0DB7f06SpQaPh+LO9cFjZqwYaSrBXrta6G6V/zuAYp2Zx8cvZtX9YhqCVVrG+kB3
|
44
|
+
jskwPBvw8jW4bFmc/enWyrRAHvcEytFnqXTjpQhU2YM1O46MIwx1tt6GSp4aPgpQ
|
45
|
+
STic0qiQv5j6yIwrJxF+KvvO3qmuOJMi+qbs+1xhdsNE1swMfi9tBoCidEC7tx/0
|
46
|
+
O9dzVB/zAgMBAAGjgfowgfcwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYB
|
47
|
+
Af8CAQAwHQYDVR0OBBYEFAhu2Lacir/tPtfDdF3MgB+oL1B6MEcGA1UdIARAMD4w
|
48
|
+
PAYEVR0gADA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNv
|
49
|
+
bS9yZXBvc2l0b3J5LzAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3JsLmdsb2Jh
|
50
|
+
bHNpZ24ubmV0L3Jvb3QuY3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB8GA1UdIwQY
|
51
|
+
MBaAFGB7ZhpFDZfKiVAvfQTNNKj//P1LMA0GCSqGSIb3DQEBBQUAA4IBAQAiXMXd
|
52
|
+
PfQLcNjj9efFjgkBu7GWNlxaB63HqERJUSV6rg2kGTuSnM+5Qia7O2yX58fOEW1o
|
53
|
+
kdqNbfFTTVQ4jGHzyIJ2ab6BMgsxw2zJniAKWC/wSP5+SAeq10NYlHNUBDGpeA07
|
54
|
+
jLBwwT1+170vKsPi9Y8MkNxrpci+aF5dbfh40r5JlR4VeAiR+zTIvoStvODG3Rjb
|
55
|
+
88rwe8IUPBi4A7qVPiEeP2Bpen9qA56NSvnwKCwwhF7sJnJCsW3LZMMSjNaES2dB
|
56
|
+
fLEDF3gJ462otpYtpH6AA0+I98FrWkYVzSwZi9hwnOUtSYhgcqikGVJwQ17a1kYD
|
57
|
+
sGgOJO9K9gslJO8k
|
58
|
+
-----END CERTIFICATE-----
|
59
|
+
- |
|
60
|
+
-----BEGIN CERTIFICATE-----
|
61
|
+
MIIEyjCCA7KgAwIBAgISESEyE8rNriS4+1dc8jOHEUL8MA0GCSqGSIb3DQEBBQUA
|
62
|
+
MFExCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMScwJQYD
|
63
|
+
VQQDEx5HbG9iYWxTaWduIENvZGVTaWduaW5nIENBIC0gRzIwHhcNMTMxMDExMTUx
|
64
|
+
NTM4WhcNMTYxMDExMTUxNTM4WjBgMQswCQYDVQQGEwJVUzEWMBQGA1UECBMNTWFz
|
65
|
+
c2FjaHVzZXR0czEPMA0GA1UEBxMGQm9zdG9uMRMwEQYDVQQKEwpSYXBpZDcgTExD
|
66
|
+
MRMwEQYDVQQDEwpSYXBpZDcgTExDMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
67
|
+
CgKCAQEAhD//7+739c69hssg0mD6CXgf2JkuWTcU81dgD7aKcoEPqU8e1FseBvDW
|
68
|
+
/Q5fNK2H2NgHV/Msn18zXuK0PkaJXqj/vDsuKB3Hq0BiR2AwyDdEw8K5MK5bgQc2
|
69
|
+
tmcVtEAejRoy1Uv5UyfaAYAxG6zsma3buV1fjnEAC3VouRg4+EX/f65H/a6srntK
|
70
|
+
5Etp3D71k2f0oUl8dOqOmSsRJQQ5zSs4ktDvpjAmsvzoA+1svceLYU95mvQsIw2T
|
71
|
+
edpmibGMwGw/HmgV+YWBgF5UGvax6zbC2i6DF2YHnDfkNb8/1MEIaxOTAbJTazTK
|
72
|
+
8laCQOyay6L1BNPQKjZBgOge8LZq1wIDAQABo4IBizCCAYcwDgYDVR0PAQH/BAQD
|
73
|
+
AgeAMEwGA1UdIARFMEMwQQYJKwYBBAGgMgEyMDQwMgYIKwYBBQUHAgEWJmh0dHBz
|
74
|
+
Oi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9zaXRvcnkvMAkGA1UdEwQCMAAwEwYD
|
75
|
+
VR0lBAwwCgYIKwYBBQUHAwMwPgYDVR0fBDcwNTAzoDGgL4YtaHR0cDovL2NybC5n
|
76
|
+
bG9iYWxzaWduLmNvbS9ncy9nc2NvZGVzaWduZzIuY3JsMIGGBggrBgEFBQcBAQR6
|
77
|
+
MHgwQAYIKwYBBQUHMAKGNGh0dHA6Ly9zZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2Fj
|
78
|
+
ZXJ0L2dzY29kZXNpZ25nMi5jcnQwNAYIKwYBBQUHMAGGKGh0dHA6Ly9vY3NwMi5n
|
79
|
+
bG9iYWxzaWduLmNvbS9nc2NvZGVzaWduZzIwHQYDVR0OBBYEFE536JwFx9SpaEi3
|
80
|
+
w8pcq2GRFA5BMB8GA1UdIwQYMBaAFAhu2Lacir/tPtfDdF3MgB+oL1B6MA0GCSqG
|
81
|
+
SIb3DQEBBQUAA4IBAQAGpGXHtFLjTTivV+xQPwtZhfPuJ7f+VGTMSAAYWmfzyHXM
|
82
|
+
YMFYUWJzSFcuVR2YfxtbS45P7U5Qopd7jBQ0Ygk5h2a+B5nE4+UlhHj665d0zpYM
|
83
|
+
1eWndMaO6WBOYnqtNyi8Dqqc1foKZDNHEDggYhGso7OIBunup+N4sPL9PwQ3eYe6
|
84
|
+
mUu8z0E4GXYViaMPOFkqaYnoYgf2L+7L5zKYT4h/NE/P7kj7EbduHgy/v/aAIrNl
|
85
|
+
2SpuQH+SWteq3NXkAmFEEqvLJQ4sbptZt8OP8ghL3pVAvZNFmww/YVszSkShSzcg
|
86
|
+
QdihYCSEL2drS2cFd50jBeq71sxUtxbv82DUa2b+
|
87
|
+
-----END CERTIFICATE-----
|
88
|
+
date: 2016-08-30 00:00:00.000000000 Z
|
89
|
+
dependencies:
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: bundler
|
92
|
+
requirement: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.12'
|
97
|
+
type: :development
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.12'
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: rake
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '10.0'
|
111
|
+
type: :development
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '10.0'
|
118
|
+
- !ruby/object:Gem::Dependency
|
119
|
+
name: rspec
|
120
|
+
requirement: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3.0'
|
125
|
+
type: :development
|
126
|
+
prerelease: false
|
127
|
+
version_requirements: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '3.0'
|
132
|
+
- !ruby/object:Gem::Dependency
|
133
|
+
name: rex-text
|
134
|
+
requirement: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
type: :runtime
|
140
|
+
prerelease: false
|
141
|
+
version_requirements: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
- !ruby/object:Gem::Dependency
|
147
|
+
name: rex-socket
|
148
|
+
requirement: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
type: :runtime
|
154
|
+
prerelease: false
|
155
|
+
version_requirements: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
description: |-
|
161
|
+
This library is a pure ruby implmenetation of the SSLScan tool originally written
|
162
|
+
by Ian Ventura-Whiting. It currently depends on the system version of OpenSSL
|
163
|
+
email:
|
164
|
+
- DMaloney@rapid7.com
|
165
|
+
executables: []
|
166
|
+
extensions: []
|
167
|
+
extra_rdoc_files: []
|
168
|
+
files:
|
169
|
+
- ".gitignore"
|
170
|
+
- ".rspec"
|
171
|
+
- ".travis.yml"
|
172
|
+
- CODE_OF_CONDUCT.md
|
173
|
+
- Gemfile
|
174
|
+
- README.md
|
175
|
+
- Rakefile
|
176
|
+
- bin/console
|
177
|
+
- bin/setup
|
178
|
+
- lib/rex/sslscan.rb
|
179
|
+
- lib/rex/sslscan/result.rb
|
180
|
+
- lib/rex/sslscan/scanner.rb
|
181
|
+
- lib/rex/sslscan/version.rb
|
182
|
+
- rex-sslscan.gemspec
|
183
|
+
homepage: https://github.com/rapid7/rex-sslscan
|
184
|
+
licenses: []
|
185
|
+
metadata: {}
|
186
|
+
post_install_message:
|
187
|
+
rdoc_options: []
|
188
|
+
require_paths:
|
189
|
+
- lib
|
190
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
196
|
+
requirements:
|
197
|
+
- - ">="
|
198
|
+
- !ruby/object:Gem::Version
|
199
|
+
version: '0'
|
200
|
+
requirements: []
|
201
|
+
rubyforge_project:
|
202
|
+
rubygems_version: 2.4.8
|
203
|
+
signing_key:
|
204
|
+
specification_version: 4
|
205
|
+
summary: Ruby Exploitation(REX) Library for scanning the SSL/TLS capabilities of a
|
206
|
+
server
|
207
|
+
test_files: []
|
metadata.gz.sig
ADDED