代码审计之织梦最新版后台代码注入

1、payload如下:

1
http://localhost/dede/tag_test_action.php?url=a&token=&partcode={dede:mochazz name='source' runphp='yes'}phpinfo();{/dede:mochazz}

2、效果图:

1

3、具体分析:

首先,程序最开始部分有一个 CSRF 检测函数 csrf_check() ,该函数位于 dede/config.php文件中,可以看到改文件仅仅只是判断是否设置了 $token 以及 $_SESSION[‘token’] 是否与 $token 相等。然而在初始状态下, $_SESSION[‘token’] 的值为空,所以我们就可传入 $token为空,来绕过该 CSRF 检测,即http://localhost/dede/tag_test_action.php?token=。(下面是 csrf_check() 函数,可以添加调试语句,查看初始状态下 $_SESSION[‘token’] 的值)

2

继续看 dede/tag_test_action.php 文件,初始状态下 $partcode 为空,这个变量又是我们可以控制的,先记下,等下要用到。3

继续往下看,可以看到 $typeid 变量的值为0,所以进入27行代码 else $pv = new PartView(); 我们继续跟踪 PartView()

4

PartView() 类位于 include/arc.partview.class.php 文件第22行( 在进行代码审计的时候,可以借助linux的grep目录辅助,参数R表示递归查找,参数i表示不区分大小写,参数n表示显示第几行 )

5

6

PartView() 类的构造函数中,又实例化一个 DedeTagParse 对象(如上图),该类用于定义dede标签格式,可以在 include/dedetag.class.php 文件第93行处找到。

7

回到 dede/tag_test_action.php 文件,看到程序调用了 PartView 类的 SetTemplet 方法,跟进该方法。

8

进入改方法后,会进入第一个if语句,并调用 DedeTagPages 类的 LoadSource 方法,继续跟进 LoadSource 方法。

9

可以看到(下图), LoadSource 方法先将我们 $str 写入一个inc文件,然后又调用 LoadTemplate方法,这里的$str就是我们传入的$partcode

10

LoadTemplate 先调用 SetDefault 方法做了一些初始化操作,接着进入else语句,循环读取刚刚写入文件的内容,存在 SourceString 中,然后调用 LoadCache 方法,跟进 LoadCache 方法。

11

12

LoadCache 返回false之后,就直接进入else语句,调用 ParseTemplet 方法,继续跟进。发现实例化一个 DedeAttributeParse 对象,该对象为dede的属性解析器,此时仅初始化了一些属性,暂时不跟进,继续看ParseTemplet 方法。

13

深度截图_选择区域_20180329143605

for循环代码太长,这里用省略号替代,主要的功能是遍历字符串模板,提取数据,然后调用 SaveCache 方法写入文件中,如下图:

15

16

下面有要回到 dede/tag_test_action.php 文件,执行最后一句 $pv->Display(); ,即调用 PartView 类的 Display 方法,发现该方法又调用了DedeTagParse 类的 Display 方法,继续跟进。

17

DedeTagParse 类的 Display 方法调用了 GetResult 方法,跟进

18

调用了 AssignSysTag 方法

19

AssignSysTag 方法中 $CTagDedeTag 实例化对象,这里的 $CTag->TagName 就是 $partcode 中dede:后面的那一小串单词,这里没有写else处理语句,所以我们只要不填写 global、include、foreach、var 可以不进入if、else if语句,直接来到下面的if( $CTag->GetAtt('runphp') == 'yes' ) ,跟进 GetAtt 方法(注意这里是 DedeTag 类的 GetAtt 方法,不是 DedeAttribute 类的 GetAtt 方法)

20

21

22

可以看到 DedeTag 类的 GetAtt 方法又调用了 DedeAttribute 类的 GetAtt 方法,继续跟进。

23

使用 var_dump() 来查看 $this->Items 数据类型,可以发现 $partcode 里面的name=’source’ runphp=’yes’ 会以数组键值对的形式存储在 $this->Items ,中,所以我们这里构造partcode={dede:tag name=’source’ runphp=’yes’},这样我们就进入了 AssignSysTag 方法中的 $this->RunPHP($CTag, $i) 语句。

24

25

RunPHP 方法中,$phpcode 在经过正则匹配后会被 eval 函数执行,而$phpcode变量是从 DedeTag->GetInnerText() 方法获取的,我们跟进 GetInnerText 方法。

26

27

其实这里的 $this->InnerText ,在前面的 LoadCache 方法中以及 DedeTag 类初始化中,可以找到定义。这个就是eval函数中 $phpcode 的值。

28

此次审计到此结束,整个过程有点绕,需要花一些时间,才能理清思路。

4、参考文章:

Dedecms V5.7后台任意代码执行[CVE-2018-7700]

文章作者: Mochazz
文章链接: https://mochazz.github.io/2018/03/29/dedecms最新后台getshell/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Mochazz's blog