漏洞通报信息:
https://mp.weixin.qq.com/s/_bpg8buT92dzRuGdr2ZrRg
https://mp.weixin.qq.com/s/s1yqJNOsCk-uVyCtg6SG7A
OA-2017非官网:http://www.downxia.com/downinfo/202980.html
最新版v11下载地址:http://www.tongda2000.com/download/2019.php?F=baidu_natural&K=
补丁包:http://cdndown.tongda2000.com/oasp/2019/2020_A1.rar
下载补丁包查看,发现新版v11的补丁有点多啊,又看了一下2017版本的,发现只修改了一个ispirit/im/upload.php。于是选择先分析OA2017作为切入点,方便偷懒。
基本思路:安装2017版的通达OA,提取出对应的upload.php做个代码对比,就能够知道补丁的修改点了。
安装后在webroot目录下获得了全部代码,不过代码都是zend54加密的,需要解密。
由于文件比较多,可以使用SeayDzendj本地解密:https://www.webshell.cc/6379.html
之后使用Bcompare进行代码比对,发现代码有两处变动。
第一处:
第二处:
分析:
第一处代码的修改使原本可以不包含的auth.php变成了必定包含。那我们来看一下auth.php的内容。
auth.php
显然,auth.php是用来检验用户登录状态的。那么在修复前,是if..else语句,该检测可以被绕过,从而在未登录的状态下执行后续的文件上传操作。
第二处代码的修改则是多了一句
if (strlen(urldecode($_FILES['ATTACHMENT']['name'])) != strlen($_FILES['ATTACHMENT']['name']))
{
$_FILES['ATTACHMENT']['name'] = urldecode($_FILES['ATTACHMENT']['name']);
}
如果字符串解码后的长度与原长度不同,说明可以被url解码,那么再url解码,而不是直接解码。
一开始怀疑是把shell.php文件名二次编码后绕过上传检测,然后在其他调用上传文件的页面(例如个人文件移动)中被再次解码,生成shell.php文件。 所以在此加入了检测语句,防止文件名被二次解码。但仔细一想,发现这根本没什么用呀,这里的代码解码只进行了一次,无法检测3次、4次url编码的文件名,并不能达到拦截文件名的效果。
网站内随便找点上传功能试试,也没发现什么端倪
这边疑惑了很久,搞不清它的意义。后来觉得可能这并不是漏洞的修补点,因为通达OA2017也有很多个小版本,可能小版本更新中早就已经加入了这段代码,而非此次的补丁(后续对OA v11的分析也印证了这一点)。
在url检测后,紧接着是文件上传。
$ATTACHMENTS = upload("ATTACHMENT", $MODULE, false);
尝试构造上传请求,必要的变量名有P,DEST_UID,以及一个文件的变量名ATTACHMENT。
同时上传参数值以及文件内容的请求比较少见,那么此时作为一个垃圾安全人员,如何POST值的同时加入文件上传这种问题,显然是要百度解决的。百度后,我们可以构造类似于下面这种形式的页面代码,抓取其请求进行修改。
构造请求包,上传成功。文件保存于\MYOA\attach\im\2003\目录,在原来的文件名前面加了一长串随机值,而且该目录不存在web目录下。 尝试在文件名中插入../测试是否能目录穿越,也不行。
我们再看代码,之前提到的第二处代码修改点的外围是if ($UPLOAD_MODE == '1') { }。
那么就想着尝试$UPLOAD_MODE == '1'来看看会出现什么情况,结果发现$UPLOAD_MODE这个变量是凭空出现的! 先不慌,猜想可能是从其他文件中包含过来的,或者是某种全局变量啥的。 全局搜索字段“UPLOAD_MODE”,也是什么都没有。 那么这个变量是量子力学?平行宇宙?
可以通过“变量覆盖漏洞”的知识点联想“变量注册”这个概念,参考:https://www.cnblogs.com/xiaozi/p/7768580.html
例如当register_globals=On的时候,可以直接使用$id来代替$_GET['id'] $_POST['id']接受传递过来的值。 当然不只这一种方式,此处也并非是这种方式,就不再浪费时间去找相关的代码了。 实践出真知,直接上传测试。
变量$UPLOAD_MODE注册成功,上传文件成功,实际在目录中的文件名是1123589952.12345.gif,还是仍旧保存在原来的目录。
不过只能上传小马文件,却无法触发,这就很难受。继续分析一下,会发现一个奇怪的地方,upload.php所在的目录\ispirit\im中的代码似乎是一条独立的系统,并没有用于其web页面。
这im其实是负责通达OA的客户端程序-办公精灵的后端接口。从菜单栏-附件程序可以下载
下载了客户端,不过客户端一直连不上服务。而且也为这2017版的漏洞点花了太多时间,性价比不高,就暂时停止了。
回过头来看最新版v11,我们以2017的upload.php漏洞进行切入。
查看ispirit/im/upload.php,通过对比可以发现只有一个修改点,和2017版本的补丁一样,是对auth.php的包含问题。而之前在2017版本的困惑的第二个修改点,在此却并不是补丁,代码前后一致,说明之前的想法是对的,这里并不是漏洞触发点。
然后我们对所有的补丁的文件做个统计,会发现大部分的代码修改点都是一致的
general\appbuilder\modules\officeproduct\models\OfficeProducts.php
general\netdisk\api.php
general\person_info\avatar\avatar.upload.php
general\picture\upload_new.php
general\reportshop\design\report\set_report.php
general\reportshop\workshop\report\crscell\set_report.php
general\setting_guide\update.php
general\index_simple_submit.inc.php
inc\attendance\attend.clock.funcs.php
inc\interface\interface.netdisk.funcs.php
inc\interface\interface.picture.funcs.php
inc\utility_file.php
module\AIP\upload.aip.php
task\clean\slow_logs.inc.php
上述14个文件的代码修改点:rename函数更改为td_rename
general\system\attachment\position\add.php
general\system\attachment\position\update.php
上述2个文件的代码中皆增加了如下代码检测,检测路径中的关键字。
ispirit\im\upload.php 包含auth.php,检测登录状态。
\ispirit\interface\gateway.php增加了..检测,防止跨目录包含
mobile\inc\funcs.php
general\netdisk\swfupload_new.php
这两个文件改动有些大,不再讨论。
分析一下最广泛的补丁:rename函数更改为td_rename,
rename()是php的自带函数,rename(oldname,newname)函数重命名文件或目录。若成功,则该函数返回 true。若失败,则返回 false。rename也是支持路径输入的,即可以移动文件,跨目录改名。
td_name()则是在inc\utility_file.php中被定义的:
function td_rename($oldname, $newname)
{
if (!is_uploadable($newname)) {
Message(_("禁止"), _("禁止创建此类型文件"));
Button_Back();
exit();
}
if (file_exists($oldname)) {
return rename($oldname, $newname);
}
else {
Message(_("错误"), _("原文件不存在"));
Button_Back();
exit();
}
}
有了is_uploadable()的检测, 其定义也在inc\utility_file.php中,禁止改名后的新文件以php为后缀。
很容易联想到在ispirit\im\upload.php中存在无需登录的文件上传,只不过文件名称被做了限制,而且保存目录不在网站目录下。 这时候配合rename()的改名和移动目录的能力,就能把文件名改为xx.php并放入网站目录下。不过遗憾的是,这个改名操作的漏洞点需要用户在登录状态下才能,降低了安全风险。 rename()带来的安全隐患应该普遍存在于各个web项目中。
参考:
UnicodeSec出了一个分析,https://www.cnblogs.com/potatsoSec/p/12516234.html
在ispirit/interface/gateway.php中存在的文件包含漏洞可以通过包含日志的形式触发,且无需登录。
poc:
首先构造url并访问,在日志中写入一句话
/ispirit/interface/gateway.php?json={}&aa=
然后通过如下url进行文件包含利用
/ispirit/interface/gateway.php?json={}&url=../../ispirit/../../nginx/logs/oa.access.log