10

这个漏洞,我劝你耗子尾汁

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzAwMzYxNzc1OA%3D%3D&%3Bmid=2247488799&%3Bidx=1&%3Bsn=f7c27b87fcfff0f2df53572bbdfae254
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

这是  酒仙桥六号部队  的第 127   篇文章。

全文共计3620个字,预计阅读时长11分钟

前言

最近在某论坛上看到一篇分析74cms存在模板解析漏洞的文章,74cms使用了tp3的框架,然后自己对tp框架的模板解析渲染也不是很熟,就想着学习一下这个漏洞,顺便熟悉一下tp3的模板解析。说干就干,先挑一个软一点的74cms捏一下,cms版本:v6.0.20。

漏洞分析

对一个已知漏洞进行分析比较喜欢采用溯源的方式进行,首先已知漏洞点的位置:/Application/Common/Controller/BaseController.class.php,触发方法:assign_resume_tpl。

RfUB7v6.png!mobile

在第175行调用了fetch方法,而在tp3框架内,所有的控制器都是继承自父类:/ThinkPHP/Library/Think/Controller.class.php,所以我们直接跟进到父类当中,来查看这个fetch方法到底发生了什么。这里要注意一下,传递的变量$tpl是要被解析的模板路径。

来到父类当中,可以看到这个fetch方法是来自于构造方法中实例化的view对象。

fyIVNfi.png!mobile

MZRR7vB.png!mobile

view视图类的位置在:/ThinkPHP/Library/Think/View.class.php。所以还是继续跟进到视图类当中,这里还是要注意参数的传递,在第84行中可以看到,调用fetch函数时传递三个参数,参数$templateFile是之前传递的被解析模板的路径,$content和$prefix两个参数都为空。接着来看视图类当中的fetch函数,先上代码。

2m2QJrQ.png!mobile

首先判断$content参数是否为空,根据上面的传参,$content变量为空,然后调用parseTemplate函数,传递$templateFile,跟进一下这个函数。

ZrmUfmN.png!mobile

可以看到使用is_file函数判断传递的模板是否是个文件,如果是的话直接返回,这里不管是图片文件,TXT文件等等,is_file函数都返回true,所以此处直接就返回到调用点了。接下来再看第117行,此处有一个判断,通过C方法获取模板引擎类型,看是否是使用原生的PHP模板,这个配置文件位于:/ThinkPHP/Conf/convention.php,tp3的默认配置是“Think”。

eYbuyez.png!mobile

所以经过判断之后会直接进入到视图解析标签模块,也就是第125,126行。其中125行是将参数组合成一个数组,其中$this-tVar是存储模板中的变量,$temlateFile存储的还是被解析模板的路径,$content和$prefix依旧为空。配置debug之后,可以清楚的看到参数的传递情况。

UNbqQbr.png!mobile

之后进入这个HOOK::listen方法,方法位置:/ThinlPHP/Library/Think/Hook.class.php。

iMBRVj.png!mobile

这个HOOK类是一个行为扩展,在TP3中称之为钩子,当我们传递了一个“view_parse“参数之后,实际就是触发了一个”view_parse“事件,此时TP3会进入到Hook::listen方法,查找$tags变量中有没有绑定”view_parse”的方法,然后遍历$tags的属性,执行Hook::exec方法。这里我们通过debug看一下整个Hook::listen的执行过程以及中间参数的变化。

iaA3ui.png!mobile

在上面的参数传递过程中可以看到view_parse事件绑定了ParseTemplateBehavior的行为方法。在执行Hook::exec方法时,$name传递绑定的行为方法,$params传递的是一个引用。之后就进入到exec方法之中。

6byEv22.png!mobile

在exec方法之中规定所有行为扩展的入口是run方法,根据上一步的参数传递,在118行中实例化ParseTemplateBehavior行为对象,然后调用该对象的run方法,并将引用的$param参数传递进去。该类的路径:/ThinkPHP/Library/Behavior/ParseTemplateBehavior.class.php。

因为所有行为扩展的入口都是run方法,所有我们直接看run方法就可以了。

EjUZZ3z.png!mobile

在上面的分析中我们知道模板引擎是“Think“,所以程序会进入到25至29行,又因为新解析一个模板是没有缓存对象的,所以此处直接进入第31行,这里传递的参数需要注意,在第22行中,$ data[‘content’]为空,所以此时$ content变量的值是$_data[‘file’],即解析模板的路径。然后进入模板编译与加载的fetch方法。该方法路径:/ThinkPHP/Library/Think/Template.class.php。

接下来看到Template类,首先看到fetch函数。

7jqyYfe.png!mobile

此处$templateFile变量还是待解析模板的路径,然后在第76行调用loadTemplate方法,再继续跟进该函数。在loadTemplate方法中主要关注以下几个地方:在第92行读取模板文件的内容,赋值给变量$tmpContent。

MbMVvyB.png!mobile

在第113行通过compiler方法对模板内容进行编译,最后第114行将编译后的结果进行存储,并且返回编译后模版文件的路径。

FNfMruY.png!mobile

此处还要继续跟进compiler方法,查看模板的编译过程。

UrY3Qf2.png!mobile

在编译过程中第130行会将没有经过任何过滤的模板内容拼接进入模板代码当中,然后将模板内容直接返回。此处通过debug可以直观的看到返回值得内容。

返回结果之后再回到上面的loadTemplate方法,第114行将结果进行存储,生成模板文件,然后将文件的路径返回给fetch方法。

在fetch方法获取到路径之后,第77行调用load方法加载模板,漏洞产生的原因就在此处。Load方法的路径:/ThinkPHP/Library/Think/Storage/Driver/File.class.php

FfqY3yI.png!mobile

第80行代码是为了调试自己加的,在进入load方法之后首先判断变量是否为空,然后对变量进行extract的解析,此处其实会出现另外一个问题,变量覆盖,如果刚好能够覆盖$_filename变量,那么又是一个漏洞。不过此处没有这么复杂,不需要进行变量覆盖,因为恶意代码以及被写入缓存的模板文件中,而第81行直接通过include对模板文件进行包含,这就造成了漏洞。接着看一下缓存的模板里面的内容:

IjaAVb3.png!mobile

然后此处第一行会校验我们是否是从入口访问到的模板,避免我们直接访问模板,这里没有什么影响。只要include能成功执行到第二行,我们的恶意代码就会执行,创建一个sites.txt文件,内容为Runoob。

到上面这一步这个漏洞的利用过程已经分析完毕了,可以使用一张流程图来看一下整个过程。

VbEJnqY.png!mobile

原生框架测试

通过上面的梳理,我们可以看到其实整个漏洞触发的过程都是在TP3框架内部进行的,是因为在进行模板解析之前没有控制传入的模板路径,解析过程中没有过滤模板内的文件内容,解析完成之后直接通过include方式将模板文件进行了包含。那么如果我们跳出74cms,直接看TP3框架,理论上也是会存在这个漏洞的。所以接下来下载原生的TP3框架,自己写一个触发漏洞的方法。将Home模块的Index控制器修改,代码如下:

FRZN3qQ.png!mobile

触发控制器的请求:index.php?m=home&c=index&a=index&variable=1&tpl=./1.txt,其中1.txt是要被解析的带有恶意代码的模板文件,内容如下:

ERneI36.png!mobile

如果代码被执行就会在根目录下写入一个sites.txt文件,内容是Runoob。之后在前台触发以下漏洞,看是否会生成该文件。执行后的情况:

RJRnYfZ.png!mobile

成功生成sites.txt文件。

ARJbu2m.png!mobile

查看一下缓存的模板文件的内容。

veqyAfR.png!mobile

可以看到在使用了原生的TP3模板之后,也是能成功触发漏洞的。而且经过调试,整个漏洞触发的过程与之前分析的一致,也就不在赘述了。那么我们也可以得到结论,这个漏洞其实是TP3框架本身的问题,不是特定发生在某些cms上的,如果有程序使用了TP3的框架,而且使用了基本控制器中的fetch方法,且模板的路径可以由用户定义就有可能触发这个漏洞,这样的话漏洞的影响范围就变得更加广泛了。

小小的总结

在这一次审计的过程当中,熟悉了TP3框架对模板的解析过程,受益匪浅。这个漏洞产生的根本原因还是过滤不严格造成了任意文件包含漏洞,属于框架本身的漏洞。在调试过程中还走进了一个误区,最开始使用phpinfo()作为恶意模板的内容,但是多次尝试都没有看到有结果输入,这不是因为phpinfo没有执行,而是因为模板定义存在问题,所以不会产生回显,其实phpinfo本身在被包含时就已经执行了,所以之后将payload换成了写入一个新的文件,这样即使不能回显也可以看到恶意代码是否执行。还有一点困难就是调试过程中函数的跳转,参数的传递次数过多,如果不仔细调试容易跟丢执行流程。不管如何,这一次的调试还是有着巨大的收获。

6ZbAv2B.png!mobile

jayqYjj.png!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK