【代码审计】文件包含 or 代码执行
2022-7-10 17:50:4 Author: 白帽子(查看原文) 阅读量:15 收藏

声明:Tide安全团队原创文章,转载请声明出处!文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!

1. 简介

ThinkCMF是一款基于PHP+MYSQL开发的中文内容管理框架,底层采用ThinkPHP3.2.3构建。ThinkCMF提出灵活的应用机制,框架自身提供基础的管理功能,而开发者可以根据自身的需求以应用的形式进行扩展。每个应用都能独立的完成自己的任务,也可通过系统调用其他应用进行协同工作。在这种运行机制下,开发商场应用的用户无需关心开发SNS应用是如何工作的,但他们之间又可通过系统本身进行协调,大大的降低了开发成本和沟通成本。

2. 影响版本

ThinkCMF X1.6.0

ThinkCMF X2.1.0

ThinkCMF X2.2.0

ThinkCMF X2.2.1

ThinkCMF X2.2.2

ThinkCMF X2.2.3

3. 复现环境

我这里下载的2.2.0版本,下载地址为:thinkcmfx2.2.0

安装过程就略过了

4. 漏洞复现

0×01

payload:http://www.code.com/?a=display&templateFile=user/articles&prefix=%27%27&&content=../../../../../../../../../etc/passwd

0×02

payload: http://www.code.com/?a=display&templateFile=user/articles&prefix=%27%27&&content=%3C?php%20file_put_contents(%27test.php%27,%27%3C?php%20phpinfo();%20?%3E%27);

上述请求发送后,会在网站根目录生成test.php,我们访问一下:

5. 漏洞分析

合格性的网站程序,都有统一的入口文件【index类似的文件,后缀可能不同】,并且都是基于mvc的设计理念,通常入口文件主要完成:

定义框架路径、项目路径(可选)

定义调试模式和应用模式(可选)

定义系统相关常量(可选)

载入框架入口文件(必须)

就此项目入口文件,就定义了项目的路径,如下图:

配置文件,web的配置文件都会定义一些,默认值,下面图中定义了默认的路由值,Portal默认模块,index默认控制器,index默认方法

代码执行文件,有了上面的两步分析,就能找到项目默认执行文件是哪个了,在哪个位置。如下图:代码很简洁,在index的方法里,只有display的方法,显然$this->display 的调用,在本类中没有定义这个方法,所以,猜测父级应该有定义这个函数。

父类里的display方法,不出所料的,找到了父级的display方法,猛地一看,这个方法里面,就一句代码,又出来了display的方法,这个方法,调用的方式为parent的什么,这里是继承了此类的父级display。

二级父类display方法,这里方法体又来了个display,但是,这里不再是另外父级的display了,而是直接调用的其他类,这个类$this->view可以通过print打印出来,设置个断点,再一个简单粗暴的方式,就是直接搜索项目,关键词display,对结果一一排查。

view.class.php类里的display方法,通过上面的调试啊,搜索排查啊,找到了这个类的display方法,很好的注释,我们把目光,重点放到$this->fetch函数上,这里有文件的读写操作【前期猜测,后期证实】。

调用fetch方法,此方法中的重点是第二个if这条语句的判断,这里走的是else,下图有有说明,那hook::listen这里监听的是什么,其学名叫钩子,调用的格式和参数——Hook::listen('钩子名称','参数(引用)','额外参数','是否一次有效返回值');钩子名称传了view_parse,参数$params,这个参数里面,有两个位置要画下重点,一个是键名为file,值为templateFile,这里是get传参的文件名称,第二个是键名为content,值为get传参的content内容值。后面有关于这两个值的逻辑判断。

执行else,原因如下:

代码执行到listen方法体,重点是self::exec方法。

到exec方法,name值为Behavior/ParseTemplateBehavior,下面有截图,在if判断时,符合第一个逻辑,$class这个参数值就是这个类的命名空间,$tag直接赋值为run,也就是实例化这个类,调用run方法。

符合Behavior这种路径的,统一调用run方法

到这个类的run方法,上面强调了两个参数,一个是file,一个是content,这里很好的做了是文件,和内容的判断,如果内容为空,$content的最终内容是存在的文件内容,如果有值,则为传参里的content值,这里优先级content参数值,优于file值,但如果这两个重复了,那就是file的值了。因为后面还有这个判断,这里卖个关子。这里执行else的分支,这里直接断点调试就出来了,打个echo 123,也能做判断。

Template类文件fetch方法,在实验过程中,标注了文件写入的位置,和文件包含的位置,【记忆力差,这里主动标注的】所以,这里直接看具体的方法内容。

loadTemplate方法,这是第一个方法,这里就解释了,我上面卖的关子,如果是文件,且存在,就获取文件的内容,否则,是get 参数content的值。

Storage类里的put方法,这是存储的类,调用了静态put方法。所谓静态方法,使用了static的修饰词,并且不需要实例化的对象,其次,配合使用了魔术方法__callstatic,感觉这里溜得飞起。这里的self::$handler是file的类,call_user_func_array()此函数调用规则,第一个参数数组类型,数组里面放的是类,和类方法,第二个参数为调用方法的参数,这里不好理解,可以多读两遍,多品品,也可自行查手册。

File类里的put方法,通过上面的call_user_func_array方法,到了file类的put方法,很简单,监测目录是否存在,创建目录,赋值权限,写入文件,到此,就是完成了文件写操作。 

最后执行include加载,就有了页面的显示效果。

6. 总结

这里放一张流程图,给上面的陈述做个导图,直观一点,如下:

这里可以体会逆推的方式,挖掘漏洞,这张图就点到这了。

菜鸟的我学到了什么:

1、 子类调用父类,public属性方法,直接访问

2、 静态魔术方法 __callStatic

3、 只需要一个payload,就可以实现网上的文件包含、写shell

E

N

D

guān

zhù

men

Tide安全团队正式成立于2019年1月是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。

对安全感兴趣的小伙伴可以关注团队官网: http://www.TideSec.com 或长按二维码关注公众号:


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650246168&idx=2&sn=4b3ea1103e5d471d2251935ca470f568&chksm=82ea57b1b59ddea7720dc5a0a53d07a1ae7864d6f649cf0ae0a358bc751da4ebe5e1c6cbda94#rd
如有侵权请联系:admin#unsafe.sh