当前位置: 首页 > news >正文

phpcms头像上传漏洞引发的故事

目录

关键代码

第一次防御

第一次绕过

第二次防御

第二次绕过

第三次防御

第三次绕过

如何构造一个出错的压缩包

第四次防御

第四次绕过


 

本篇文章是参考某位大佬与开发人员对于文件包含漏洞的较量记录下的故事,因为要学习文件包含漏洞,就将大佬的文件作为参考来通过学习+练习的方式复现一下这次较量的全过程

故事还要从phpcms曾经火极一时的头像上传漏洞说起,因为这个漏洞,互联网上大量站点被黑,影响极为恶劣。

简单来说phpcms对头像上传是这么处理:上传上去的zip文件,它先解压好,然后删除非图片文件。

关键代码

前端代码:

<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta http-equiv="X-UA-Compatible" content="ie=edge" /><title>文件上传章节练习题</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"><style type="text/css">.login-box{margin-top: 100px;height: 500px;border: 1px solid #000;}body{background: white;}.btn1{width: 200px;}.d1{display: block;height: 400px;}</style>
</head>
<body><form method="post" action="upload.php" enctype="multipart/form-data"><input type="file" name="file" value=""/><input type="submit" name="submit" value="upload"/></form>
</body>
</html>

后端代码:

<?php
header("Content-Type:text/html; charset=utf-8");
require_once('pclzip.lib.php');$file = $_FILES['file'];
if ($file['size'] == 0) {exit("请勿上传空文件");
}
$name = $file['name'];
$dir = 'uploads/';
$ext = strtolower(substr(strrchr($name, '.'), 1));function check_dir($dir)
{$handle = opendir($dir);while (($f = readdir($handle)) !== false) {if (!in_array($f, array('.', '..'))) {$ext = strtolower(substr(strrchr($f, '.'), 1));if (!in_array($ext, array('jpg', 'gif', 'png'))) {unlink($dir . $f);}}}}if (!is_dir($dir)) {mkdir($dir);
}$temp_dir = $dir . 'member/1/';
if (!is_dir($temp_dir)) {mkdir($temp_dir);
}if (in_array($ext, array('zip', 'jpg', 'gif', 'png'))) {if ($ext == 'zip') {// $zip = new ZipArchive;// if(!$zip->open($file['tmp_name'])) {//     echo "fail";//     return false;// }// if(!$zip->extractTo($temp_dir)) {//     // check_dir($temp_dir);//     exit('fail to zip');// }$archive = new PclZip($file['tmp_name']);if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) {exit("解压失败");}check_dir($temp_dir);exit('上传成功!');} else {move_uploaded_file($file['tmp_name'], $temp_dir . '/' . $file['name']);check_dir($temp_dir);exit('上传成功!');}
} else {exit('仅允许上传zip、jpg、gif、png文件!');
}

第一次防御

访问前端页面发现我们是可以上传一个文件的,因此这样就可以创建一个phpinfo.php文件和一个1.jpg文件,然后放在php文件夹中,然后将该文件夹压缩上传:

d778b2561c36425da6dcd7fdddd9eae9.png

然后尝试进行上传该压缩包:

上传完成后可以看到上传成功了

b0182a870abe46ffb50f0c5342d26aa6.png

然后我们可以在那个对应的目录中进行查看上传后的文件内容:

6e86e784bc294075bba4a32ce9ecc5bc.png

可以看到上传后的文件只剩下了一个1.jpg文件,和它一起在压缩包中的phpinfo.php文件被删除了

第一次绕过

这里那就无法上传php文件了,那么我们就真的就没有办法了吗?大佬告诉了我们解决方案:

通过代码审计,可以看到,它删除的时候没有递归删除也没有删除文件夹

这样,只要我们的webshell放在压缩包的文件夹中,就可避免被删除了。

因此这样就可以创建一个phpinfo文件夹里面再放入一个phpinfo.php文件一个单独的1.jpg文件,然后放在php2文件夹中,然后将该文件夹压缩上传:

8e38a5b9bcd3490c97e13dccd785633b.png

上传完成后可以再去uploads文件夹中查看就会发现已经成功的上传了php文件了

82b8291ca7764749b52a77b6345f9742.png

可以看到,现在就成功的上传了phpinfo文件并且里面的phpinfo.php文件保留了下来,并且1.jpg也上传了,这样就成功的绕过了限制

第二次防御

后面网页管理员对网页进行了一些安全加固,将后端的检查文件代码修改为下列的形式:

function check_dir($dir)
{$handle = opendir($dir);while (($f = readdir($handle)) !== false) {if (!in_array($f, array('.', '..'))) {if (is_dir($dir . $f)) {check_dir($dir . $f . '/');} else {$ext = strtolower(substr(strrchr($f, '.'), 1));if (!in_array($ext, array('jpg', 'gif', 'png'))) {unlink($dir . $f);}}}}
}

第一次绕过的方法的根本原因是因为没有考虑文件在文件夹中的情况,只删除了压缩包根目录下的非法文件,而没有删除其文件夹中的非法文件。

所以补丁就采用了递归删除的方式,将压缩包中所有非法文件删除。

但是采用了递归的方式就真的可以防御好了吗?并不是的

第二次绕过

因为通过分析代码后发现,删除的方式是先上传后删除,那么就可以尝试利用上传成功和删除的时间差来尝试访问该文件

因此这里使用Burpsuite来进行不断的上传,然后手动的访问一下上传后的文件看看会不会成功呢

首先使用Burpsuite对上传页面进行抓包,然后发送到intruder页面,添加payload:

07fe8d37aac241529b9541e37209a2bd.png

然后设置发送1000个包,然后开始

b6b098e4a9c841c9ab0210182659190d.png

下面就是尝试快速的不断的访问一下上传后的php文件:
d3f7e8fde29748fabcdb9cf7e8a4eccf.png

可以看到通过上传后与删除的时间差是可以访问到php文件的,因此这种防御方法也是可以绕过的

第三次防御

上面的绕过方法出现后,后端人员也是快速的修改了,增加了一下代码:

$temp = FCPATH.'cache/attach/'.md5(uniqid().rand(0, 9999)).'/';

就是将压缩包放在一个随机命名的文件夹中再解压缩,这样你猜不到访问地址也就没法去暴力getshell了。

第三次绕过

通过对后端代码的审计后,发现当解压发生失败时,就退出解压缩过程。

这也是一个很平常的思路,失败了肯定要报错并退出,因为后面的代码没法运行了。

但是,程序员不会想到,有些压缩包能在解压到一半的时候出错。

什么意思,也就说我可以构造一个“出错”的压缩包,它可以解压出部分文件,但绝对会在解压未完成时出错。这是造成了一个状况:我上传的压缩包被解压了一半,webshell被解压出来了,但因为解压失败这里exit($this->pclzip->zip(true));退出了程序执行,后面一切的删除操作都没有了作用。

如何构造一个出错的压缩包

因为这里我们首先构造一个解压会出错的压缩包,这里龙哥给我们交了几种好用的方法:

我这里就以两个解压的程序作为例子:

  1. Windows下的7zip

  2. PHP自带的ZipArchive库

7zip的容忍度很低,只要压缩包中某一个文件的CRC校验码出错,就会报错退出。

如何修改压缩包里文件的CRC校验码呢?可以使用010editor。

我们先准备两个文件,一个PHP文件1.php,一个文本文件2.txt,其中1.php是webshell。

然后将这两个文件压缩成shell.zip。

然后我们用010editor打开shell.zip,可以看到右下角有这个文件的格式信息,它被分成5部分

ba5bc0681f4d4bd19f637a428c89cdbe.png

c4d9256f7c224229ac5a3d45943bf6a1.png

我们打开第4部分,其中有个deCrc,我们随便把值改成其他的值,然后保存。

cc6235d8fd7f43fbb19fa06254014e58.png

此时用7zip解压就会出错,解压出的1.php是完好的,2.txt是一个空文件。

6ae51932c36848389f856d96d2bb8c07.png

我们再用PHP自带的ZipArchive库,测试这个zip,发现解压并没有出错,这也说明ZipArchive的容忍度比较高。

那么我们又如何让ZipArchive出错呢?最简单的方法,我们可以在文件名上下功夫。

比如,Windows下不允许文件名中包含冒号(:)

我们就可以在010editor中将2.txt的deFileName属性的值改成“2.tx:”。

1ac6ddd74ee24213a87bd1f50ef03461.png

此时解压就会出错,但1.php被保留了下来。

注:在Linux下也有类似的方法,我们可以将文件名改成5个斜杠(/)。

因此这里我们第三次绕过的思路有出来了,使用一个会报错的zip文件,上传后会将php文件解压问成功,进行报错了,因此php文件会被保留

第四次防御

这里的漏洞也是被后端人员发现后修复了,因此将代码修改为下面的形式:

        if(!$zip->extractTo($temp_dir)) {check_dir($temp_dir);exit('fail to zip');}

可以看到,它会对解压失败的文件也进行一次检查,将里面非图片的文件删除 

第四次绕过

但是这样真的就安全了吗?答案是不安全的

压缩包中通常是不含有诸如“../”、“..”这种文件名的,但通常不含有不代表不能含有。

如果把压缩包中某文件名改成../../../../../index.php,是不是就能直接把你首页变成我的webshell呀?

这就是因为抄袭者并没有真正领悟zip这个类的使用方法,导致了这个安全问题。

先把自己的shell改名字成aaaaaaaaaaaaaaaaaaaa.php

之所以起这个名字,就是预留一些空间,方便我之后将文件名改成../../../aaaaaaaaaaa.php而不用怕字符串长度不对。

把文件直接打包成zip,用010editor打开:

将画框的俩文件名的前9个字符改成../../../

a3eea48173024483b5f80bdf26f98df8.png

然后再上传就可以成功上传了

总结一下:

1、没有对上传的文件进行递归删除,导致文件夹中的目录可以上传

2、先上传,后删除,可以利用时间竞争来访问上传后的文件

3、对上传后的文件进行一个重命名,随机名称,可以利用解压报错绕过

4、对解压后的文件进行检查,可以利用../../路径穿越进行绕过

到此这个黑客大佬与后端开发人员的对抗就结束了,但是还有很多web页面存储漏洞仍然存在着漏洞等着我们去发现

 

相关文章:

phpcms头像上传漏洞引发的故事

目录 关键代码 第一次防御 第一次绕过 第二次防御 第二次绕过 第三次防御 第三次绕过 如何构造一个出错的压缩包 第四次防御 第四次绕过 本篇文章是参考某位大佬与开发人员对于文件包含漏洞的较量记录下的故事&#xff0c;因为要学习文件包含漏洞&#xff0c;就将大佬…...

二叉树|二叉树理论基础、二叉树的递归遍历

代码随想录 (programmercarl.com) 树和二叉树 1.树的基本概念 1.1树的定义 1.2树的逻辑表示方法 1.3树的基本术语 1.4树的性质 1.5树的基本运算 1.6树的存储结构 2.二叉树的概念和性质 2.1二叉树的定义 2.2二叉树的性质 2.3二叉树与树、森林之间的转换 3.二叉树的…...

JavaScript 语法-对象

对象 JavaScript 中的对象是一组键值对的集合&#xff0c;其中每个键都是字符串&#xff0c;每个值可以是任意类型。 对象是由一些属性和方法组成的集合&#xff0c;属性可以用来存储数据&#xff0c;方法可以用来操作数据。 属性和方法使用“.”来访问 // 创建一个对象 let …...

代码随想录阅读笔记-哈希表【四数之和】

题目 给定一个包含 n 个整数的数组 nums 和一个目标值 target&#xff0c;判断 nums 中是否存在四个元素 a&#xff0c;b&#xff0c;c 和 d &#xff0c;使得 a b c d 的值与 target 相等&#xff1f;找出所有满足条件且不重复的四元组。 注意&#xff1a;答案中不可以包…...

JVM学习——双亲委派机制

简而言之就是为了防止与Java固有全类名重复&#xff0c;而导致系统崩坏所设立的机制。 当类加载器接收到加载类的任务时&#xff0c;首先会向上请求&#xff0c;一直请求到引导类加载器&#xff0c;如果引导类加载器无法加载&#xff0c;就会逐层返回让类加载器自己执行&#…...

【Paper Reading】6.RLHF-V 提出用RLHF的1.4k的数据微调显著降低MLLM的虚幻问题

分类 内容 论文题目 RLHF-V: Towards Trustworthy MLLMs via Behavior Alignment from Fine-grained Correctional Human Feedback 作者 作者团队&#xff1a;由来自清华大学和新加坡国立大学的研究者组成&#xff0c;包括Tianyu Yu, Yuan Yao, Haoye Zhang, Taiwen He, Y…...

Aloudata 倾力打造,《Data Fabric 白皮书 2.0》正式发布

数字经济时代&#xff0c;越来越多企业开始寻求全新的数据管理范式&#xff0c;以更有效地管理、利用不断增长的数据资产。在此背景下&#xff0c;Data Fabric 的概念应运而生&#xff0c;被视为面向未来的数据管理解决方案。 距离第一版白皮书问世已经过去一年多时间&#xff…...

docker内部无法使用ping等网络工具解决方案

通常docker内部没有网络&#xff0c;所以我们先离线安装需要的依赖包&#xff0c;然后再使用sh脚本容器内部访问宿主机同网络端其他服务器ip,实现监测远程ip telnet包依赖于netbase包&#xff0c;但是netbase包没有安装。你需要先安装netbase包&#xff0c;然后再尝试安装teln…...

后端工程师快速使用vue和Element

文章目录 Vue1 Vue概述2 快速入门3 Vue指令3.1 v-bind和v-model3.2 v-on3.3 v-if和v-show3.4 v-for3.5 案例 4 生命周期 Element快速使用1 Element介绍2 快速入门3 当前页面中嵌套另一个页面案例代码案例截图 Vue 1 Vue概述 通过我们学习的htmlcssjs已经能够开发美观的页面了…...

自学rabbitmq入门到精通

交换机的fault &#xff08;发布与订阅模式&#xff09; 因为消息是由生产者发送给excahnge&#xff0c;exchange发送给队列&#xff0c; 然后由队列发送给消费者的。 展示使用图形化界面使用fanout模式。 创建交换机 然后创建三个队列&#xff0c;绑定对应的交换机&#xff…...

由浅到深认识C语言(13):共用体

该文章Github地址&#xff1a;https://github.com/AntonyCheng/c-notes 在此介绍一下作者开源的SpringBoot项目初始化模板&#xff08;Github仓库地址&#xff1a;https://github.com/AntonyCheng/spring-boot-init-template & CSDN文章地址&#xff1a;https://blog.csdn…...

python爬虫(9)之requests模块

1、获取动态加载的数据 1、在开发者工具中查看动态数据 找到csdn的门户的开发者工具后到这一页面。 2、加载代码 import requests headers {User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36…...

phpstudy自定义安装mysql8.3并启动

phpstudy自定义安装mysql8.3并启动 先去官网:https://dev.mysql.com/downloads/下载压缩包文件 然后按下面的图片一步一步操作 选择版本&#xff0c;选择第一个压缩包文件&#xff0c;下载 下载完成后&#xff0c;解压到phpstudy环境目录下&#xff0c;如下图 然后进入mysq…...

Netty 学习资料

Netty 学习资料 搜集了一下Java网络库Netty的学习资料&#xff0c;整理如下&#xff0c;有空花时间研究一下。 1、Netty学习手册 《尚硅谷 Netty 核心技术及源码剖析》课程学习手册 本课程不适合零基础的学员&#xff0c;需要掌握常用的设计模式和数据结构 掌握 Java 的面向对…...

【概率论中的两种重要公式:全概率和贝叶斯】

贝叶斯公式&#xff08;Bayes’ Theorem&#xff09;是概率论中的一条重要定理&#xff0c;用于计算条件概率。它描述了在已知某一事件发生的条件下&#xff0c;另一事件发生的概率。贝叶斯公式如下所示&#xff1a; P ( A ∣ B ) P ( B ∣ A ) ⋅ P ( A ) P ( B ) P(A|B) \…...

python中的闭包

一、闭包 1、作用域 在Python代码中&#xff0c;作用域分为两种情况&#xff1a;全局作用域 与 局部作用域 2、变量的作用域 在全局定义的变量 > 全局变量 在局部定义的变量 > 局部变量 3、全局变量与局部变量的访问范围 ① 在全局作用域中可以访问全局变量&#…...

成功解决RuntimeError: OpenSSL 3.0‘s legacy provider failed to load

报错 RuntimeError: OpenSSL 3.0s legacy provider failed to load. This is a fatal error by default, but cryptography supports running without legacy algorithms by setting the environment variable CRYPTOGRAPHY_OPENSSL_NO_LEGACY. If you did not expect this er…...

【 React 】React 组件之间如何通信?

相关文章&#xff1a; React Context的使用方法 react Provider Consumer 使用方法 1. 是什么 我们将组件间通信可以拆分为两个词&#xff1a; 组件通信 组件是vue中最强大的功能之一&#xff0c;同样组件化是React的核心思想 相比vue&#xff0c;React的组件更加灵活和多样…...

汇总全网免费API,持续更新(新闻api、每日一言api、音乐。。。)

Public&FreeAPI 网址&#xff1a;apis.whyta.cn &#xff08;推荐&#xff09; UomgAPI 网址&#xff1a;https://api.uomg.com 教书先生 网址&#xff1a;https://api.oioweb.cn/ 山海API https://api.shserve.cn/ 云析API铺 https://api.a20safe.com/ 韩小韩…...

Android SystemServer进程解析

SystemServer进程在android系统中占了举足轻重的地位&#xff0c;系统的所有服务和SystemUI都是由它启动。 一、SystemServer进程主函数流程 1、主函数三部曲 //frameworks/base/services/java/com/android/server/SystemServer.java /** * The main entry point from zy…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

JavaSec-RCE

简介 RCE(Remote Code Execution)&#xff0c;可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景&#xff1a;Groovy代码注入 Groovy是一种基于JVM的动态语言&#xff0c;语法简洁&#xff0c;支持闭包、动态类型和Java互操作性&#xff0c…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

Oracle11g安装包

Oracle 11g安装包 适用于windows系统&#xff0c;64位 下载路径 oracle 11g 安装包...