lib/scanner/generic.rb in yawast-0.7.0.beta1 vs lib/scanner/generic.rb in yawast-0.7.0.beta2

- old
+ new

@@ -1,5 +1,7 @@ +# frozen_string_literal: true + require 'ipaddr_extensions' require 'json' require 'public_suffix' module Yawast @@ -7,11 +9,11 @@ class Generic def self.head_info(head, uri) begin server = '' powered_by = '' - cookies = Array.new + cookies = [] pingback = '' frame_options = '' content_options = '' csp = '' backend_server = '' @@ -24,25 +26,25 @@ Yawast::Utilities.puts_info 'HEAD:' head.each do |k, v| Yawast::Utilities.puts_info "\t\t#{k}: #{v}" Yawast::Shared::Output.log_value 'http', 'head', k, v - server = v if k.downcase == 'server' - powered_by = v if k.downcase == 'x-powered-by' - pingback = v if k.downcase == 'x-pingback' - frame_options = v if k.downcase == 'x-frame-options' - content_options = v if k.downcase == 'x-content-type-options' - csp = v if k.downcase == 'content-security-policy' - backend_server = v if k.downcase == 'x-backend-server' - runtime = v if k.downcase == 'x-runtime' - xss_protection = v if k.downcase == 'x-xss-protection' - via = v if k.downcase == 'via' - hpkp = v if k.downcase == 'public-key-pins' - acao = v if k.downcase == 'access-control-allow-origin' + server = v if k.casecmp('server').zero? + powered_by = v if k.casecmp('x-powered-by').zero? + pingback = v if k.casecmp('x-pingback').zero? + frame_options = v if k.casecmp('x-frame-options').zero? + content_options = v if k.casecmp('x-content-type-options').zero? + csp = v if k.casecmp('content-security-policy').zero? + backend_server = v if k.casecmp('x-backend-server').zero? + runtime = v if k.casecmp('x-runtime').zero? + xss_protection = v if k.casecmp('x-xss-protection').zero? + via = v if k.casecmp('via').zero? + hpkp = v if k.casecmp('public-key-pins').zero? + acao = v if k.casecmp('access-control-allow-origin').zero? - if k.downcase == 'set-cookie' - #this chunk of magic manages to properly split cookies, when multiple are sent together + if k.casecmp('set-cookie').zero? + # this chunk of magic manages to properly split cookies, when multiple are sent together v.gsub(/(,([^;,]*=)|,$)/) { "\r\n#{$2}" }.split(/\r\n/).each do |c| cookies.push(c) Yawast::Shared::Output.log_append_value 'http', 'head', 'cookies', c end @@ -50,53 +52,43 @@ end puts '' if server != '' Yawast::Scanner::Plugins::Servers::Apache.check_banner(server) - Yawast::Scanner::Php.check_banner(server) + Yawast::Scanner::Plugins::Servers::Generic.check_banner_php(server) Yawast::Scanner::Plugins::Servers::Iis.check_banner(server) Yawast::Scanner::Plugins::Servers::Nginx.check_banner(server) Yawast::Scanner::Plugins::Servers::Python.check_banner(server) - if server == 'cloudflare-nginx' + if server == 'cloudflare' Yawast::Utilities.puts_info 'NOTE: Server appears to be Cloudflare; WAF may be in place.' puts end end - if powered_by != '' - Yawast::Utilities.puts_warn "X-Powered-By Header Present: #{powered_by}" - end + Yawast::Utilities.puts_warn "X-Powered-By Header Present: #{powered_by}" if powered_by != '' - if xss_protection == '0' - Yawast::Utilities.puts_warn 'X-XSS-Protection Disabled Header Present' - end + Yawast::Utilities.puts_warn 'X-XSS-Protection Disabled Header Present' if xss_protection == '0' - unless pingback == '' - Yawast::Utilities.puts_info "X-Pingback Header Present: #{pingback}" - end + Yawast::Utilities.puts_info "X-Pingback Header Present: #{pingback}" unless pingback == '' unless runtime == '' if runtime.is_number? Yawast::Utilities.puts_warn 'X-Runtime Header Present; likely indicates a RoR application' else Yawast::Utilities.puts_warn "X-Runtime Header Present: #{runtime}" end end - unless backend_server == '' - Yawast::Utilities.puts_warn "X-Backend-Server Header Present: #{backend_server}" - end + Yawast::Utilities.puts_warn "X-Backend-Server Header Present: #{backend_server}" unless backend_server == '' - unless via == '' - Yawast::Utilities.puts_warn "Via Header Present: #{via}" - end + Yawast::Utilities.puts_warn "Via Header Present: #{via}" unless via == '' if frame_options == '' Yawast::Utilities.puts_warn 'X-Frame-Options Header Not Present' else - if frame_options.downcase == 'allow' + if frame_options.casecmp('allow').zero? Yawast::Utilities.puts_vuln "X-Frame-Options Header: #{frame_options}" else Yawast::Utilities.puts_info "X-Frame-Options Header: #{frame_options}" end end @@ -105,21 +97,15 @@ Yawast::Utilities.puts_warn 'X-Content-Type-Options Header Not Present' else Yawast::Utilities.puts_info "X-Content-Type-Options Header: #{content_options}" end - if csp == '' - Yawast::Utilities.puts_warn 'Content-Security-Policy Header Not Present' - end + Yawast::Utilities.puts_warn 'Content-Security-Policy Header Not Present' if csp == '' - if hpkp == '' - Yawast::Utilities.puts_warn 'Public-Key-Pins Header Not Present' - end + Yawast::Utilities.puts_warn 'Public-Key-Pins Header Not Present' if hpkp == '' - if acao == '*' - Yawast::Utilities.puts_warn 'Access-Control-Allow-Origin: Unrestricted' - end + Yawast::Utilities.puts_warn 'Access-Control-Allow-Origin: Unrestricted' if acao == '*' puts '' unless cookies.empty? Yawast::Utilities.puts_info 'Cookies:' @@ -127,120 +113,37 @@ cookies.each do |val| Yawast::Utilities.puts_info "\t\t#{val.strip}" elements = val.strip.split(';') - #check for secure cookies + # check for secure cookies if elements.include?(' Secure') || elements.include?(' secure') if uri.scheme != 'https' Yawast::Utilities.puts_warn "\t\t\tCookie with Secure flag sent over non-HTTPS connection" end else Yawast::Utilities.puts_warn "\t\t\tCookie missing Secure flag" end - #check for HttpOnly cookies + # check for HttpOnly cookies unless elements.include?(' HttpOnly') || elements.include?(' httponly') Yawast::Utilities.puts_warn "\t\t\tCookie missing HttpOnly flag" end - #check for SameSite cookies + # check for SameSite cookies unless elements.include?(' SameSite') || elements.include?(' samesite') Yawast::Utilities.puts_warn "\t\t\tCookie missing SameSite flag" end end puts '' end puts '' - rescue => e + rescue => e # rubocop:disable Style/RescueStandardError Yawast::Utilities.puts_error "Error getting head information: #{e.message}" raise end end - - def self.check_options(uri) - begin - req = Yawast::Shared::Http.get_http(uri) - req.use_ssl = uri.scheme == 'https' - headers = Yawast::Shared::Http.get_headers - res = req.request(Options.new('/', headers)) - - if res['Public'] != nil - Yawast::Utilities.puts_info "Public HTTP Verbs (OPTIONS): #{res['Public']}" - Yawast::Shared::Output.log_value 'http', 'options', 'public', res['Public'] - - puts '' - end - if res['Allow'] != nil - Yawast::Utilities.puts_info "Allow HTTP Verbs (OPTIONS): #{res['Allow']}" - Yawast::Shared::Output.log_value 'http', 'options', 'allow', res['Allow'] - - puts '' - end - end - end - - def self.check_trace(uri) - begin - req = Yawast::Shared::Http.get_http(uri) - req.use_ssl = uri.scheme == 'https' - headers = Yawast::Shared::Http.get_headers - res = req.request(Trace.new('/', headers)) - - if res.body.include?('TRACE / HTTP/1.1') && res.code == '200' - Yawast::Utilities.puts_warn 'HTTP TRACE Enabled' - puts "\t\t\"curl -X TRACE #{uri}\"" - - puts '' - end - - Yawast::Shared::Output.log_value 'http', 'trace', 'raw', res.body - Yawast::Shared::Output.log_value 'http', 'trace', 'code', res.code - end - end - - def self.check_propfind(uri) - begin - req = Yawast::Shared::Http.get_http(uri) - req.use_ssl = uri.scheme == 'https' - headers = Yawast::Shared::Http.get_headers - res = req.request(Propfind.new('/', headers)) - - if res.code.to_i <= 400 && res.body.length > 0 && res['Content-Type'] == 'text/xml' - Yawast::Utilities.puts_warn 'Possible Info Disclosure: PROPFIND Enabled' - puts "\t\t\"curl -X PROPFIND #{uri}\"" - - puts '' - end - - Yawast::Shared::Output.log_value 'http', 'propfind', 'raw', res.body - Yawast::Shared::Output.log_value 'http', 'propfind', 'code', res.code - Yawast::Shared::Output.log_value 'http', 'propfind', 'content-type', res['Content-Type'] - Yawast::Shared::Output.log_value 'http', 'propfind', 'length', res.body.length - end - end - end - - #Custom class to allow using the PROPFIND verb - class Propfind < Net::HTTPRequest - METHOD = 'PROPFIND' - REQUEST_HAS_BODY = false - RESPONSE_HAS_BODY = true - end - - #Custom class to allow using the OPTIONS verb - class Options < Net::HTTPRequest - METHOD = 'OPTIONS' - REQUEST_HAS_BODY = false - RESPONSE_HAS_BODY = true - end - - #Custom class to allow using the TRACE verb - class Trace < Net::HTTPRequest - METHOD = 'TRACE' - REQUEST_HAS_BODY = false - RESPONSE_HAS_BODY = true end end end