Class: Yawast::Scanner::Ssl
- Inherits:
-
Object
- Object
- Yawast::Scanner::Ssl
- Defined in:
- lib/scanner/ssl.rb
Class Method Summary collapse
- .check_hsts(head) ⇒ Object
- .get_ciphers(uri) ⇒ Object
- .get_session_msg_count(uri) ⇒ Object
- .info(uri, check_ciphers, sslsessioncount) ⇒ Object
Class Method Details
.check_hsts(head) ⇒ Object
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/scanner/ssl.rb', line 163 def self.check_hsts(head) found = '' head.each do |k, v| if k.downcase.include? 'strict-transport-security' found = "#{k}: #{v}" end end if found == '' Yawast::Utilities.puts_warn 'HSTS: Not Enabled' else Yawast::Utilities.puts_info "HSTS: Enabled (#{found})" end puts '' end |
.get_ciphers(uri) ⇒ Object
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/scanner/ssl.rb', line 107 def self.get_ciphers(uri) puts 'Supported Ciphers (based on your OpenSSL version):' dns = Resolv::DNS.new() ip = dns.getaddresses(uri.host)[0] #find all versions that don't include '_server' or '_client' versions = OpenSSL::SSL::SSLContext::METHODS.find_all { |v| !v.to_s.include?('_client') && !v.to_s.include?('_server')} versions.each do |version| #ignore SSLv23, as it's an auto-negotiate, which just adds noise if version.to_s != "SSLv23" ciphers = OpenSSL::SSL::SSLContext.new(version).ciphers puts "\tChecking for #{version.to_s} suites (#{ciphers.count} possible suites)" ciphers.each do |cipher| #try to connect and see what happens begin socket = TCPSocket.new(ip.to_s, uri.port) context = OpenSSL::SSL::SSLContext.new(version) context.ciphers = cipher[0] ssl = OpenSSL::SSL::SSLSocket.new(socket, context) ssl.hostname = uri.host ssl.connect if cipher[2] < 112 || cipher[0].include?('RC4') #less than 112 bits or RC4, flag as a vuln Yawast::Utilities.puts_vuln "\t\tVersion: #{ssl.ssl_version.ljust(7)}\tBits: #{cipher[2]}\tCipher: #{cipher[0]}" elsif cipher[2] >= 128 #secure, probably safe Yawast::Utilities.puts_info "\t\tVersion: #{ssl.ssl_version.ljust(7)}\tBits: #{cipher[2]}\tCipher: #{cipher[0]}" else #weak, but not "omg!" weak. Yawast::Utilities.puts_warn "\t\tVersion: #{ssl.ssl_version.ljust(7)}\tBits: #{cipher[2]}\tCipher: #{cipher[0]}" end ssl.sysclose rescue OpenSSL::SSL::SSLError => e unless e..include?('alert handshake failure') || e..include?('no ciphers available') || e..include?('wrong version number') Yawast::Utilities.puts_error "\t\tVersion: #{ssl.ssl_version.ljust(7)}\tBits: #{cipher[2]}\tCipher: #{cipher[0]}\t(Supported But Failed)" end rescue => e Yawast::Utilities.puts_error "\t\tVersion: #{''.ljust(7)}\tBits: #{cipher[2]}\tCipher: #{cipher[0]}\t(#{e.message})" ensure ssl.sysclose unless ssl == nil end end end end puts '' end |
.get_session_msg_count(uri) ⇒ Object
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/scanner/ssl.rb', line 181 def self.get_session_msg_count(uri) # this method will send a number of HEAD requests to see # if the connection is eventually killed. puts 'TLS Session Request Limit: Checking number of requests accepted...' count = 0 begin req = Yawast::Shared::Http.get_http(uri) req.use_ssl = uri.scheme == 'https' req.keep_alive_timeout = 600 headers = Yawast::Shared::Http.get_headers req.start do |http| 10000.times do |i| http.head(uri.path, headers) # hack to detect transparent disconnects if http.instance_variable_get(:@ssl_context).session_cache_stats[:cache_hits] != 0 raise 'TLS Reconnected' end count += 1 if i % 20 == 0 print '.' end end end rescue => e puts Yawast::Utilities.puts_info "TLS Session Request Limit: Connection terminated after #{count} requests (#{e.message})" return end puts Yawast::Utilities.puts_warn 'TLS Session Request Limit: Connection not terminated after 10,000 requests' Yawast::Utilities.puts_warn 'TLS Session Request Limit: If server supports 3DES, may be affected by SWEET32' end |
.info(uri, check_ciphers, sslsessioncount) ⇒ Object
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/scanner/ssl.rb', line 8 def self.info(uri, check_ciphers, sslsessioncount) begin socket = TCPSocket.new(uri.host, uri.port) ctx = OpenSSL::SSL::SSLContext.new ctx.ciphers = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:ciphers] ssl = OpenSSL::SSL::SSLSocket.new(socket, ctx) ssl.hostname = uri.host ssl.connect cert = ssl.peer_cert unless cert.nil? Yawast::Utilities.puts_info 'Found X509 Certificate:' Yawast::Utilities.puts_info "\t\tIssued To: #{cert.subject.common_name} / #{cert.subject.organization}" Yawast::Utilities.puts_info "\t\tIssuer: #{cert.issuer.common_name} / #{cert.issuer.organization}" Yawast::Utilities.puts_info "\t\tVersion: #{cert.version}" Yawast::Utilities.puts_info "\t\tSerial: #{cert.serial}" Yawast::Utilities.puts_info "\t\tSubject: #{cert.subject}" #check to see if cert is expired if cert.not_after > Time.now Yawast::Utilities.puts_info "\t\tExpires: #{cert.not_after}" else Yawast::Utilities.puts_vuln "\t\tExpires: #{cert.not_after} (Expired)" end #check for SHA1 & MD5 certs if cert.signature_algorithm.include?('md5') || cert.signature_algorithm.include?('sha1') Yawast::Utilities.puts_vuln "\t\tSignature Algorithm: #{cert.signature_algorithm}" else Yawast::Utilities.puts_info "\t\tSignature Algorithm: #{cert.signature_algorithm}" end Yawast::Utilities.puts_info "\t\tKey: #{cert.public_key.class.to_s.gsub('OpenSSL::PKey::', '')}-#{get_x509_pub_key_strength(cert)}" Yawast::Utilities.puts_info "\t\t\tKey Hash: #{Digest::SHA1.hexdigest(cert.public_key.to_s)}" Yawast::Utilities.puts_info "\t\tExtensions:" cert.extensions.each { |ext| Yawast::Utilities.puts_info "\t\t\t#{ext}" unless ext.oid == 'subjectAltName' } #alt names alt_names = cert.extensions.find {|e| e.oid == 'subjectAltName'} unless alt_names.nil? Yawast::Utilities.puts_info "\t\tAlternate Names:" alt_names.value.split(',').each { |name| Yawast::Utilities.puts_info "\t\t\t#{name.strip.delete('DNS:')}" } end hash = Digest::SHA1.hexdigest(cert.to_der) Yawast::Utilities.puts_info "\t\tHash: #{hash}" puts "\t\t\thttps://censys.io/certificates?q=#{hash}" puts "\t\t\thttps://crt.sh/?q=#{hash}" puts '' end cert_chain = ssl.peer_cert_chain if cert_chain.count == 1 #HACK: This is an ugly way to guess if it's a missing intermediate, or self-signed #tIt looks like a change to Ruby's OpenSSL wrapper is needed to actually fix this right. if cert.issuer == cert.subject Yawast::Utilities.puts_vuln "\t\tCertificate Is Self-Singed" else Yawast::Utilities.puts_warn "\t\tCertificate Chain Is Incomplete" end puts '' end unless cert_chain.nil? Yawast::Utilities.puts_info 'Certificate: Chain' cert_chain.each do |c| Yawast::Utilities.puts_info "\t\tIssued To: #{c.subject.common_name} / #{c.subject.organization}" Yawast::Utilities.puts_info "\t\t\tIssuer: #{c.issuer.common_name} / #{c.issuer.organization}" Yawast::Utilities.puts_info "\t\t\tExpires: #{c.not_after}" Yawast::Utilities.puts_info "\t\t\tKey: #{c.public_key.class.to_s.gsub('OpenSSL::PKey::', '')}-#{get_x509_pub_key_strength(c)}" Yawast::Utilities.puts_info "\t\t\tSignature Algorithm: #{c.signature_algorithm}" Yawast::Utilities.puts_info "\t\t\tHash: #{Digest::SHA1.hexdigest(c.to_der)}" puts '' end puts '' end puts "\t\tQualys SSL Labs: https://www.ssllabs.com/ssltest/analyze.html?d=#{uri.host}&hideResults=on" puts '' if check_ciphers get_ciphers(uri) end ssl.sysclose get_session_msg_count(uri) if sslsessioncount rescue => e Yawast::Utilities.puts_error "SSL: Error Reading X509 Details: #{e.message}" end end |