OctoberCMS 任意文件读取(CVE-2020-15246)
前言
🎯 影响范围
受影响产品: October CMS
受影响版本: 1.0.421 至 1.0.468 版本
安全版本: 1.0.469 及以上版本,以及 1.1.0 及以上版本
漏洞复现
环境搭建
主要如下:
PHP 7.3.4
october 1.0.467
Python3
漏洞代码
入口主要在 modules/cms/classes/Controller.php,OctoberCMS 前台访问一个页面时,会进入 Controller::run($url),它会根据 URL 找到对应的 CMS 页面,然后执行页面逻辑
1 | public function run($url = '/') { |
在 runPage() 里,October 会判断这是不是 AJAX 请求。如果是 AJAX 请求,就会先执行 AJAX handler,而不是正常渲染完整页面
1 | if ($useAjax && $ajaxResponse = $this->execAjaxHandlers()) { |
也就是说,只要访问的是一个存在的前台 CMS 页面,攻击者不一定需要后台登录,就有机会触发前台 AJAX 处理流程
在源码里的 getAjaxHandler() 中,OctoberCMS 会检查请求是否是 Request::ajax() && Request::isMethod('post')
然后从请求头中取 X_OCTOBER_REQUEST_HANDLER
也就是 OctoberCMS AJAX 框架使用的 handler 名称,更关键的是,源码中存在一个通用 handler:
1 | public function onAjax() |
所以攻击者不一定需要页面上真的定义了某个复杂 handler,只要构造一个 October AJAX 请求,使用通用的 onAjax,就能让流程继续往下走
概念上类似这样:
1 | POST /某个存在的前台页面 HTTP/1.1 |
这里真正危险的是最后这个:X-OCTOBER-REQUEST-PARTIALS, execAjaxHandlers() 中,OctoberCMS 会读取这个请求头
然后把里面的 partial 名称拆出来逐个渲染:
1 | foreach ($partialList as $partial) { |
当 AJAX 流程调用 $this->renderPartial($partial) 之后,如果它不是组件 partial,也不是 layout partial,就会走主题 partial 加载
1 | Partial::loadCached($this->theme, $name) |
其中 Partial.php 定义了 partial 的目录名 protected $dirName = 'partials';
以正常情况下,OctoberCMS 应该只从类似下面的目录读取 partial themes/当前主题/partials/
但漏洞的关键是:如果 $name 中带有路径穿越,底层读取流程没有及时拦截,它就可能逃出 partials 目录。
官方补丁显示,修复点在 src/Halcyon/Builder.php。Build 469 的安全修复提交说明里明确提到:改进了 FileDatasource 的路径校验,并清理了 Halcyon Builder。([GitHub][2])
漏洞修复前,Builder::find($fileName) 会调用:
1 | return $this->whereFileName($fileName)->first(); |
而旧版 whereFileName($fileName) 大致是:
1 | public function whereFileName($fileName) |
它直接接受了外部传入的 $fileName,然后进入后续文件读取流程,没有先调用 validateFileName()
OctoberCMS 的 CMS 文件对象最终会走到 FileDatasource
官方源码中,FileDatasource::selectOne() 会构造文件路径,然后读取文件内容:
1 | $path = $this->makeFilePath($dirName, $fileName, $extension); |
而 makeFilePath() 的核心逻辑是路径拼接:
1 | return $this->basePath.'/'.$dirName.'/'.$fileName.'.'.$extension; |
但如果 fileName 可控,并且包含路径回退,就可能变成概念上的 themes/demo/partials/../../../../某个敏感文件
POC
1 | curl.exe -X POST "http://127.0.0.1:8000/" -H "X-Requested-With: XMLHttpRequest" -H "X-OCTOBER-REQUEST-HANDLER: onAjax" -H "X-OCTOBER-REQUEST-PARTIALS: ../../../.env" -d " " |
