THINKCMFX 任意文件包含漏洞复现

Payload: http://127.0.0.1/?a=display&templateFile=README.md

漏洞分析

根据payload可以快速定位到漏洞位于display方法中, ThinkCMF默认模块Portal, 控制器Index, 文件位于Application/Portal/Controller/IndexController.class.php, 在其继承的父类中定位到display方法。

该方法中又调用了父类的display方法, 并且传入的第一个参数调用了parseTemplate方法, 先跟进此方法。

该方法用于定位模板文件, 模板存在则返回模板文件名, 反之抛出不存在模板异常, 此时再跟进Controller类的display方法。

跟进View类的display方法,$this->fetch(‘README’, ‘’,’’)

跟进View类fetch方法, Hook::listen(‘view_parse’, $param),$param为关联数组, 其file键的值为传入的模板名

跟进Hook类listen方法

跟进Hook类exec方法

跟进ParseTemplateBehavior类的run方法

跟进Template类的fetch方法

先跟进其调用的loadTemplate方法

回到fetch方法跟进Storage的load方法

最终File类的load方法使用include包含了模板缓冲文件

总结

调用栈

  • 进入HomeBaseController类的display方法, 调用该类下的parseTemplate方法定位模板文件获取其返回值传入调用的Controller类display方法第一个参数
  • Controller类display方法调用View类display方法
  • View类display方法调用fetch方法解析模板
  • View类fetch方法调用Hook类listen方法
  • Hook类listen方法调用exec方法, 进入ParseTemplateBehavior类的run方法
  • 调用Template类的fetch方法
  • fetch方法先调用loadTemplate方法编译模板内容并返回缓存文件
  • 进入Storage类load方法, 由于该类不存在此静态方法触发__callstatic魔术方法, 在此魔术方法中使用回调函数call_user_func_array调用File类的load方法
  • File类load方法将接收的$vars里的数组注册为变量导致变量覆盖, 然后进行文件包含, 整个调用栈中没有对模板文件进行一个判断或校验导致可以包含任意文件