tl;dr: We found four vulnerabilities in NagiosXI, and chained them together to create a root RCE exploit, available here. Vulnerability chaining can increase the risk posed by individual vulns, it takes a village to raise a root RCE etc. etc. If you’re running NagiosXI <= 5.4.12, update.

If you perform application vulnerability assessments, odds are at some point you’ve experienced some push-back on reported vulnerabilities in the form of arm-chair threat modeling along the lines of “Well, you could only do x with y, so it’s probably not a must-fix”. This statement is probably overly dismissive in most cases, in part because it neglects the possible impact of vulnerability chaining.

Vulnerability chaining is the process of leveraging multiple vulnerabilities to achieve something beyond the scope of any individual vuln in the chain. A good examples of vulnerability chaining have been written about here. Offensive Security’s AWAE is basically a course about vulnerability chaining. Another good example of vulnerability chaining (hopefully) is the NagiosXI root RCE exploit, that you’re about to read about.

The Nagios XI exploit we’ve written leverages four vulnerabilities:

First, We’ll touch on each vulnerability is isolation, followed by an outline of how we chained these web application vulns together to achieve root RCE on the NagiosXI application server. If you’d like to follow along at home, you can grab a vulnerable version of NagiosXI here:

And the exploit in question here:

CVE-2018-8734 - SQL injection (unauthenticated)

An unauthenticated GET to <nagiosxi_host>/nagiosql/admin/helpedit.php gives a response with a 302 response code which redirects back to /nagiosql/index.php. In a browser, this might appear to be normal behavior for a webapp that’s redirecting unauthenticated sessions back to auth, but an intercepting web proxy allows us to inspect the response and determine that this response also contains a moderately broken form. A quick pass at this form gives us the following form submission POST to helpedit.php, which contains a vulnerable selInfoKey1 parameter.

POST /nagiosql/admin/helpedit.php HTTP/1.1
Host: <nagiosxi_host>
Content-Type: application/x-www-form-urlencoded


Since we’re performing a whitebox test of this application, we can inspect the db logs and determine exactly where the injection occurs (For a in depth look at these kinds whitebox SQLi techniques, have a look at our Unitrends 9 SQLi post). After some analysis of the database logs, we were able to confirm that selInfoKey1 is vulnerable to SQLi. However we also discovered that the database user used to issue this injectable query (nagiosql) did not have sufficient permissions to perform many of the actions that we would be interested in as an attacker such as adding users, dumping password hashes, or enumerating sessions.

[+] It’s Unauthenticated SQL injections!

[-] The DB user doesn’t have privileges to do anything too interesting.

CVE-2018-8733 - authentication bypass

The CVE description for this vulnerability is perhaps misleading, since it’s not as though we’re able to bypass authentication globally with this vulnerability. Instead, we have a similar situation to the previously discussed CVE, whereby a GET request to <nagiosxi_host>/nagiosql/admin/settings.php returns a form with a 302 response code. A little meddling with the returned form gives us the following POST request to settings.php:

POST /nagiosql/admin/settings.php HTTP/1.1
Host: <nagiosxi_host>
Content-Type: application/x-www-form-urlencoded


Nothing here is injectable, some of the settings don’t take, and others simply break nagiosql. What’s significant for us is the ability to set the database user account. If we could use this form to get the nagiosql db user more permissions, then maybe we could access parts of the database with our SQLi that were previously inaccessible. It turns out we can do just that; The ssh credentials to the appliance are set to root:nagiosxi by default, and these credentials are reused for the root database user.

[+] We can change the DB user account!

[+] We can maybe break the application configuration and cause a denial of service (DoS) situation!

[-] Taken alone, this might seem to be a DoS, at worst.

CVE-2018-8735 - command injection (authenticated)

This command injection vulnerability was discovered mostly through source code analysis of application .php files. This one is useful for us, since it affects a wide swath of NagiosXI versions, and runs as user ‘nagiosxi’ (as opposed to ‘apache’). After some source code analysis, testing and hand waving, we get the following vulnerable POST request, which is vulnerable to authenticated command injection:

POST /nagiosxi/backend/index.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Cookie: nagiosxi=eb8pa9098grmgummu2pnofq3f5
Content-Length: 66


These POST requests get back responses that don’t immediately suggest command injection:

<?xml version="1.0" encoding="utf-8"?>

but white-box testing allows us to simply touch a file to /tmp, and then verify it’s existence out of band:

[root@nagiosxi_host tmp]# ls -l
-rw-r--r--  1 nagios nagios       0 Apr 13 02:21 testing

[+] We have command injection!

[-] It’s authenticated, and requires admin level authorization.

CVE-2018-8736 - local privilege escalation

Finally, we went looking for some local privilege escalation, for reasons which may be becoming obvious at this point. Generally, a good first place to look when looking for local privesc on a linux environment is the sudoers file. Here’s an excerpt of the Nagios /etc/sudoers file:

NAGIOSXI ALL = NOPASSWD:/usr/local/nagiosxi/html/includes/components/profile/
NAGIOSXI ALL = NOPASSWD:/usr/local/nagiosxi/scripts/
NAGIOSXI ALL = NOPASSWD:/usr/local/nagiosxi/scripts/
NAGIOSXI ALL = NOPASSWD:/usr/local/nagiosxi/scripts/ *
NAGIOSXI ALL = NOPASSWD:/usr/local/nagiosxi/scripts/
NAGIOSXI ALL = NOPASSWD:/usr/local/nagiosxi/scripts/ *

This is very interesting to us, especially if file permissions on those files allow write access to the nagiosxi user, which, in fact, they do:

[root@nagiosxi_host ]# ls -l /usr/local/nagiosxi/scripts/
-rwxr-xr-x 1 nagios nagios   1664 Dec 28  2016
-rwxr-xr-x 1 nagios nagios   2303 Dec 28  2016
-rwxr-xr-x 1 nagios nagios   2681 Dec 28  2016
-rwxr-xr-x 1 nagios nagios   1010 Dec 28  2016
-rwxr-xr-x 1 nagios nagios   5673 Dec 28  2016

So, we have a fairly straight forward instance of local privilege escalation. All that user nagiosxi needs to do to execute commands as root is place them into one of the scripts listed in the sudoers file, then invoke that script via password-less sudo.

[+] It’s local privesc! User nagiosxi can easily escalate to root.

[-] It’s local privesc and anyone running commands as nagiosxi is already an administrative user on this system.

Putting it all together!

Individually these vulnerabilities carry a lot of caveats. We can modify some application parameters unauthenticated, but it’s mostly good for breaking the application. We have SQLi but it can’t touch nagiosxi authentication data. We have command injection, but it requires an authenticated administrative session. We have local privesc, but you need command line access to the application server. It may seem counter intuitive then, that taken together, these vulnerabilities provide us with everything we need to put together an unauthenticated root remote command execution exploit!

Taking a look at the finished exploit is probably the best way to get a feel for the details, but general process is outlined below:

  • STEP 0: Validate the proper nagios version - All of the listed vulnerabilities work on NagiosXI versions (5.2.6 <= x <= 5.4) The version string can be parsed out of <nagiosxi_host>/nagiosxi/login.php.

  • STEP 1: Change the database user to root - Using CVE-2018-8733, we can modify the nagiosql database user to be root, granting it sufficient authorization to access nagiosxi authentication data.

  • STEP 2: Leverage SQLi to access API keys - Now that the database user has sufficient privileges, we use CVE-2018-8734 to perform a SQL injection which returns all unique API keys in the system. The application seems to ship with an administrative API key already in the database, so this is a pretty reliable option.

  • STEP 3: Use an API key to add an administrative user - This one isn’t a vulnerability, just an API which can be used to add an administrative user, if you’ve got the proper API key.

  • STEP 4: Log in - Now that we have an administrative user in the system, we can log in. At this point, we’ve effectively bypassed authentication to the nagiosxi portion of the application.

  • STEP 5: Inject a command + elevate privileges - Here, we use CVE-2018-8735, our command injection vulnerability, to execute a command. We could establish a low priv reverse shell then elevate privileges once we’ve got a session on the application server, but our PoC does it all in one go, by placing the desired command into an ad hoc privesc script:

cp /usr/local/nagiosxi/scripts/ /usr/local/nagiosxi/scripts/ &&
echo "{your_command_here}" > /usr/local/nagiosxi/scripts/ &&
sudo /usr/local/nagiosxi/scripts/ &&
mv /usr/local/nagiosxi/scripts/ /usr/local/nagiosxi/scripts/

This script leverages CVE-2018-8736 by placing the desired command into /usr/local/nagiosxi/scripts/, then invoking that script with a password-less sudo. The first and last lines backup and restore the original script, respectively. It’s a fairly long command, but it works consistently here.


We’re done! Following the steps above, we were able to convert 4 vulnerabilities with a lot of caveats and mixed severities into 1 root RCE with no caveats. What conclusions should we draw from this? I can think of a couple.

  1. Vulnerabilities exist in the larger context of an application, not in isolation, and they should be treated as such. Failure to remediate seemingly low risk vulnerabilities can have consequences.

  2. If you’re running NagiosXI version (5.2.6 <= x <= 5.4.12), you should update.