## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Report include Msf::Auxiliary::Scanner def initialize super( 'Name' => 'FortiMail Unauthenticated Login Bypass Scanner', 'Description' => %q{ This module attempts to detect instances of FortiMail vulnerable against an unauthenticated login bypass (CVE-2020-9294). }, 'Author' => [ 'Mike Connor', # Initial Vulnerability discovery 'Juerg Schweingruber ', # Vulnerability Re-Discovery 'Patrick Schmid ' # Exploit Development & MSF module ], 'References' => [ ['CVE', '2020-9294'], ['URL', 'https://www.fortiguard.com/psirt/FG-IR-20-045'], ['URL', 'https://www.redguard.ch/blog/2020/07/02/fortimail-unauthenticated-login-bypass/'] ], 'License' => MSF_LICENSE ) register_options([ OptString.new('TARGETURI', [true, 'Path to the FortiMail admin page', '/admin/AdminLogin.html']), Opt::RPORT(443) ]) register_advanced_options([ OptBool.new('SSL', [true, 'Negotiate SSL connection', true]) ]) end def target_url proto = 'http' if (rport == 443) || ssl proto = 'https' end uri = normalize_uri(datastore['URI']) "#{proto}://#{vhost}:#{rport}#{uri}" end def run_host(ip) vprint_status("Checking vulnerability at #{ip}") uri = normalize_uri(target_uri.path) res = send_request_raw({ 'method' => 'GET', 'uri' => uri }) return :abort unless res # prints default connection error messages if res.code == 301 vprint_error("#{ip} - Page redirect to #{res.headers['Location']}") return :abort end unless res.code == 200 vprint_bad("#{ip} - No version of FortiMail detected") return :abort end version_raw = res.body[/fml-admin-login-(\d+).js/, 1] version = version_raw.to_i unless res.body.include?('newpassword') && (version.between?(140, 160) || version.between?(730, 745) || version.between?(250, 263)) print_bad("#{ip} - Not vulnerable version (Build: #{version_raw}) of FortiMail detected") return :abort end print_good("#{ip} - Vulnerable version (Build: #{version_raw}) of FortiMail detected") report_vuln( host: rhost, port: rport, name: 'FortiMail Login Bypass', refs: references ) rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout # noop rescue ::Timeout::Error, ::Errno::EPIPE # noop rescue ::OpenSSL::SSL::SSLError => e return if (e.to_s.match(/^SSL_connect /)) # strange errors / exception if SSL connection aborted end end