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

news2025/1/10 16:59:08

目录

关键代码

第一次防御

第一次绕过

第二次防御

第二次绕过

第三次防御

第三次绕过

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

第四次防御

第四次绕过


 

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

故事还要从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页面存储漏洞仍然存在着漏洞等着我们去发现

 

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

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

相关文章

什么是 HTTPS?它是如何解决安全性问题的?

什么是 HTTPS&#xff1f; HTTPS&#xff08;HyperText Transfer Protocol Secure&#xff09;是一种安全的通信协议&#xff0c;用于在计算机网络上安全地传输超文本&#xff08;如网页、图像、视频等&#xff09;和其他数据。它是 HTTP 协议的安全版本&#xff0c;通过使用加…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Grid)

网格容器&#xff0c;由“行”和“列”分割的单元格所组成&#xff0c;通过指定“项目”所在的单元格做出各种各样的布局。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 仅支持GridItem…

定时执行专家:自动截屏功能在电脑监控中的卓越应用

一、软件简介 定时执行专家&#xff0c;作为一款专业级的定时任务执行软件&#xff0c;不仅功能强大&#xff0c;而且操作简便。它支持25种任务类型&#xff0c;其中自动截屏功能尤为出色。通过这一功能&#xff0c;用户可以设定定时自动截取电脑屏幕&#xff0c;从而实现对电…

Java微服务轻松部署服务器

我们在日常开发微服务之后需要再服务器上面部署&#xff0c;那么如何进行部署呢&#xff0c;先把微服务的各个服务和中间件以及对应的端口列举出来&#xff0c;都打包成镜像&#xff0c;以及前端代码部署的nginx&#xff0c;使用docker-compose启动&#xff0c;访问服务器nginx…

系统重构后,对项目定制开发的兼容性问题

公司自实施产品线战略以来&#xff0c;基本推翻了全部旧有业务模块。后续以标准产品二次开发的模式进行项目开发。但在涉及到一些旧有系统二期、三期升级改造过程中。不可避免的需要解决旧有系统的客户定制化开发兼容性问题。也就是旧有系统定制开发的模块不能丢弃。重新开发从…

C++Qt学习——QPushButton、QRadioButton(单选按钮)、QCheckBox(复选按钮)

目录 1、QPushButton 1.1、创建一个新的项目&#xff0c;转到UI界面拖一个Push Button 1.2、Push Button的常用信号主要有四个&#xff0c;分别为 clicked(), pressed(), released(), toggled() 1.2.1、按住Push Button右键转到槽&#xff0c;选择信号函数 1.2.2、在Widget…

国创证券|资源再生概念持续活跃,超越科技两连板,大地海洋等走高

资源再生概念15日盘中再度走强&#xff0c;截至发稿&#xff0c;超越科技涨停斩获两连板&#xff0c;深水海纳涨超14%&#xff0c;大地海洋涨超12%&#xff0c;华新环保涨近9%&#xff0c;天奇股份、格林美、怡球资源等涨超5%。 消息面上&#xff0c;3月13日&#xff0c;国务院…

解决jsp request.getParameter乱码问题(兼容Tomcat 6~8三个版本)

JSP页面写法&#xff1a; <% page contentType"text/html; charsetutf-8" language"java" %> <% page import"java.io.*" %> <%! int getServerVersion(HttpServletRequest request) {ServletContext application request.getS…

前端组件化:构建高效应用的艺术

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

LeetCode每日一题——两数之和

两数之和OJ链接&#xff1a;1. 两数之和 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 思路&#xff1a; 在读懂题目后很多人觉得这种题目很简单&#xff0c;但是不管怎么写&#xff0c;在VS等其他编译器上能跑成功&#xff0c;但是在LeetCode上就是没办法通过。…

Learn OpenGL 08 颜色+基础光照+材质+光照贴图

我们在现实生活中看到某一物体的颜色并不是这个物体真正拥有的颜色&#xff0c;而是它所反射的(Reflected)颜色。物体的颜色为物体从一个光源反射各个颜色分量的大小。 创建光照场景 首先需要创建一个光源&#xff0c;因为我们以及有一个立方体数据&#xff0c;我们只需要进行…

【论文阅读笔记】Attention Is All You Need

1.论文介绍 Attention Is All You Need 2017年 NIPS transformer 开山之作 回顾一下经典&#xff0c;学不明白了 Paper Code 2. 摘要 显性序列转导模型基于包括编码器和解码器的复杂递归或卷积神经网络。性能最好的模型还通过注意力机制连接编码器和解码器。我们提出了一个新…

Redis部署方式(三)主从模式

在前面单机版的基础上&#xff0c;41为主&#xff0c;30为从。 一、主从搭建 1、主Redis安装 41机器redis主要配置 requirepass redis#!_41 bind 0.0.0.0 port 6379 daemonize yes 2、从redis安装 30机器redis主要配置 requirepass redis#!_30 bind 0.0.0.0 port 6380 da…

Oracle 部署及基础使用

1. Oracle 简介 Oracle Database&#xff0c;又名 Oracle RDBMS&#xff0c;简称 Oracle Oracle系统&#xff0c;即是以Oracle关系数据库为数据存储和管理作为构架基础&#xff0c;构建出的数据库管理系统。是目前最流行的客户/服务器&#xff08;client/server&#xff09;或…

关于Transfomer的思考

为何诞生 在说transformer是什么&#xff0c;有什么优势之类的之前&#xff0c;先谈一谈它因何而诞生。transformer诞生最重要的原因是早先的语言模型&#xff0c;比如RNN&#xff0c;由于其本身的训练机制导致其并行度不高&#xff0c;特别是遇到一些长句子的情况下。其次&…

面试题手撕篇

参考博客 开始之前&#xff0c;理解递归 手写 浅拷贝 function shallow(target){if(target instanceof Array){return [...resObj]}else{return Object.assign({},target);} }手写深拷贝 const _sampleDeepClone target > {// 补全代码return JSON.parse(JSON.stringify…

深度学习神经网络训练环境配置以及演示

&#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#xff08;HPC&#xff09;开发基础教程 &#x1f380;CSDN主页 发狂的小花 &#x1f304;人生秘诀&#xff1a;学习的本质就是极致重复! 目录 1 NVIDIA Dr…

Flink源码解析(1)TM启动

首先在看之前,回顾一下akka模型: Flink通讯模型—Akka与Actor模型-CSDN博客 注:ActorRef就是actor的引用,封装好了actor 下面是jm和tm在通讯上的概念图: RpcGateway 用于定义RPC协议,是客户端和服务端沟通的桥梁。服务端实现了RPC协议,即实现了接口中定义的方法,做具…

云原生(二)、Docker基础

Docker Docker 是一种开源的容器化平台&#xff0c;用于开发、部署和运行应用程序。它允许开发者将应用程序及其所有依赖项打包到一个可移植的容器中&#xff0c;这个容器可以在任何支持 Docker 的环境中运行&#xff0c;无论是开发人员的个人笔记本电脑、测试环境、生产服务器…

逆序对的数量 刷题笔记

思路 使用归并排序 在每次返回时 更新增加答案数 因为归并排序的两个特点 第一 使用双指针算法 第二 层层返回 从局部有序合并到整体有序 例如 {4 &#xff0c;1 &#xff0c;2 &#xff0c;3} 划分到底层是四个数组 {4}&#xff0c;{1}&#xff0c;{3}, {…