#!/usr/bin/env ruby require 'rainbow' require 'ciphersurfer' require 'getoptlong' require 'json' def score_to_color(score) case score when 1...40 return "red".to_sym when 40...80 return "yellow".to_sym when 80..100 return "green".to_sym end end opts = GetoptLong.new( [ '--help', '-h', GetoptLong::NO_ARGUMENT ], [ '--version', '-v', GetoptLong::NO_ARGUMENT ], [ '--list-ciphers', '-l', GetoptLong::NO_ARGUMENT ], [ '--json', '-j', GetoptLong::NO_ARGUMENT] ) trap("INT") { puts '['+'INTERRUPTED'.color(:red)+']'; exit -1 } options={:json=>false,:list_ciphers=>false} opts.each do |opt, arg| case opt when '--help' puts "usage: ciphersurfer [-ljvh] server[:port]" puts " -l: lists supported ciphers instead of just evaluate the security level" puts " -j: formats the output using JSON" puts " -v: shows version" puts " -h: this help" exit 0 when '--version' puts "ciphersurfer " + Ciphersurfer::Version::STRING exit 0 when '--json' options[:json]=true when '--list-ciphers' options[:list_ciphers]=true end end if ( ARGV.length != 1 ) puts 'missing target'.color(:red) puts "usage: ciphersurfer [-ljvh] server[:port]" puts " -l: lists supported ciphers instead of just evaluate the security level" puts " -j: formats the output using JSON" puts " -v: shows version" puts " -h: this help" exit -1 end target = ARGV.shift host = target.split(':')[0] ||= "localhost" #fallback here should never occur... however it's better to be paranoid port = target.split(':')[1] ||= 443 # more common here puts "Evaluating secure communication with #{host}:#{port}" if ! Ciphersurfer::Scanner.alive?(host, port) exit -2 end protocol_version = [:SSLv2, :SSLv3, :TLSv1]#, :TLSv11, :TLSv12] supported_protocols = [] cipher_bits=[] ciphers=[] protocol_version.each do |version| s = Ciphersurfer::Scanner.new({:host=>host, :port=>port, :proto=>version}) s.go if (s.ok_ciphers.size != 0) supported_protocols << version cipher_bits = cipher_bits | s.ok_bits ciphers = ciphers | s.ok_ciphers end end if (options[:list_ciphers]) ciphers.each do |c| puts "[+] accepted #{c}".green end exit 0 end cert= Ciphersurfer::Scanner.cert(host, port) if ! cert.nil? a=cert.public_key.to_text key_size=/Modulus \((\d+)/i.match(a)[1] else puts "warning: the server didn't give us the certificate".color(:yellow) key_size=0 end proto_score= Ciphersurfer::Score.evaluate_protocols(supported_protocols) cipher_score= Ciphersurfer::Score.evaluate_ciphers(cipher_bits) key_score= Ciphersurfer::Score.evaluate_key(key_size.to_i) score= Ciphersurfer::Score.score(proto_score, key_score, cipher_score) if (options[:json]) a={:evaluation => Ciphersurfer::Score.evaluate(score), :score => score, :protocol_score=>proto_score, :key_exchange_score=>key_score, :cipher_score=>cipher_score} puts a.to_json exit 0 end printf "%20s : %s (%s)\n", "Overall evaluation", Ciphersurfer::Score.evaluate(score), score.to_s printf "%20s : ", "Protocol support" proto_score.to_i.times{print 'o'.color(score_to_color(proto_score))} puts ' ('+proto_score.to_s+')' printf "%20s : ", "Key exchange" key_score.to_i.times{print 'o'.color(score_to_color(key_score))} puts ' ('+key_score.to_s+')' printf "%20s : ", "Cipher strength" cipher_score.to_i.times{print 'o'.color(score_to_color(cipher_score))} puts ' ('+cipher_score.to_s+')'