该漏洞的触发点为/dede/tag_test_action.php。起因是csrf_check()的绕过,导致可执行任意代码。
查看/dede/tag_test_action.php文件的源码:


如果直接访问这个文件,会进入csrf_check()函数。提示token错误,且退出执行。
跟进csrf_check(),该函数所在文件为dede/config.php:

这里的判断只是判断token值是否和session中的token相等,还有就是判断token是否存在。就这么来看,貌似比较矛盾。Session[‘token’]的值我们不知道,我们没法伪造token。有趣的是,当我们登录之后,并不会生成session[‘token’],也就是说,这时的session[‘token’]为null。
这样的话,我们只需要构造http://localhost/a.php?token=
这样就可绕过csrf_check()

绕过之后,便可为$partcode构造payload。

首先是对数据进行初始化。跟进$pv->SetTemplet(),跟踪变量的流向:


继续跟进DedeTagParse类的LoadSource方法:


在这个方法中,我们传入的payload被写入inc文件中,继续跟踪LoadTemplate:


这里先是判断文件是否存在,很显然是存在的。随后将文件读入$this->SourceString字符串中。

继续跟踪LoadCache方法:


在这里,将数据读入缓存中。至此,数据初始化完成。

触发代码执行的点在PartView类的Display方法,源码如下:


在display()方法中再次调用DedeTagParse类中的display()方法:

跟进GetResult():


跟进AssignSysTag()方法:


最后跟进Runphp方法:


在这里,只是简单的将数据从对象中提取出来,做一些简单的字符串替换,便可成功执行代码。

综上,我们传入的$partcode变量应该符合dedecms模板格式,且带有runphp=’yes’标签。
基于此,我们可构造以下payload:
partcode={dede:field name='source' runphp='yes'}phpinfo();{/dede:field}

加上绕过csrf_check()的payload,得到最后的poc:

http://localhost/后台地址/tag_test_action.php?url=a&token=&partcode={dede:field name='source' runphp='yes'}phpinfo();{/dede:field}


利用条件:登录后台
解决方案:重新实现csrf_check()函数