文件上传漏洞-学习笔记-2
一、 理解请求与服务器行为
multipart/form-data请求结构剖析:- 边界 (Boundary):
Content-Type头中定义,用于分隔请求体中的不同部分。抓包时留意这个边界字符串。 - 部分 (Part): 每个表单字段(包括文件)是一个独立的部分。
Content-Disposition: 包含name(表单字段名) 和filename(原始文件名)。可伪造Content-Type(部分内): 每个部分(尤其是文件部分)可以有自己的Content-Type。可伪造- 数据: 文件的原始二进制内容。
- 利用点: 熟悉此结构是使用 Burp Suite 等工具手动修改请求、伪造文件名/类型、插入特殊字符(如
00截断)的基础。
- 边界 (Boundary):
PHP 后端处理流程 (
$_FILES):- 文件首先被 PHP 接收并存储在临时目录 (
upload_tmp_dir,通常是/tmp),文件名是随机的 (如phpXXXXXX)。 $_FILES超全局数组包含上传文件的信息:name(原始名),type(客户端报告的 MIME),tmp_name(服务器上的临时文件路径),error(错误码),size(大小)。- 关键函数:
move_uploaded_file($tmp_name, $destination)是将临时文件移动到最终位置的标准方式。它比copy()+unlink()更安全,因为它会检查$tmp_name是否真的是一个合法的上传文件。 - 让恶意文件内容通过检查,并最终被
move_uploaded_file(或其他文件写入函数) 移动到 Web 目录下,且文件名是我们期望的可执行后缀。
- 文件首先被 PHP 接收并存储在临时目录 (
临时文件的捕获与分析 (
/tmp):- 时机: 临时文件只在 PHP 脚本处理上传期间存在,脚本结束即被删除。
- 工具 (
inotifywait- Linux):1
2# 实时监控 /tmp 下 php 开头的临时文件创建事件
sudo inotifywait -m /tmp -e create -e moved_to --format '%w%f' | grep --line-buffered '/tmp/php[a-zA-Z0-9]\{6\}' - 目的: 在复杂场景下,观察临时文件的确切内容、权限、以及它在被处理(如二次渲染、内容检查)前后的变化,有助于理解服务器的防御机制。看到文件名立刻
cp或cat。
二、 绕过补充
前端绕过:
- 工具: 浏览器开发者工具直接修改 HTML
accept属性或 JS;Burp Suite/OWASP ZAP 拦截并修改请求;curl命令直接构造请求。 curl示例 (伪造类型和文件名):1
curl -X POST -F "file=@shell.php;type=image/jpeg;filename=legit.jpg" http://target.com/upload
- 工具: 浏览器开发者工具直接修改 HTML
.htaccess利用 (Apache):- 前提: 目标目录 Apache 配置允许
AllowOverride(至少FileInfo或Options)。 - 核心指令:
AddType application/x-httpd-php .jpg: 让.jpg按 PHP 执行。SetHandler application/x-httpd-php: 让目录下所有文件按 PHP 执行。php_flag engine off: 尝试关闭 PHP 引擎(如果允许)。php_value auto_prepend_file /path/to/shell.php: 包含文件后门。
- 测试: 在本地 phpStudy (Apache 模式) 中修改目录的
AllowOverride配置并重启服务来验证.htaccess是否生效。
- 前提: 目标目录 Apache 配置允许
.user.ini利用细节 (PHP CGI/FPM):- 前提: PHP 以 CGI/FPM 模式运行 (Nginx+PHP-FPM, Apache+PHP-FPM 常见)。
- 核心指令:
auto_prepend_file = shell.jpg(在执行 PHP 前包含)。 - 触发条件: 目标目录下必须存在一个可被访问的
.php文件(内容无所谓,仅作触发器)。 - 缓存: 默认有 300 秒缓存 (
user_ini.cache_ttl),上传后需等待或尝试多次访问触发文件。 - 攻击流程: 上传
shell.jpg-> 上传.user.ini(内容指向shell.jpg) -> 访问该目录下的任意.php文件 -> 触发shell.jpg执行。
00截断关键点:- 版本: 主要影响 PHP < 5.3.4。
- 配置: 需要
magic_quotes_gpc = Off。 - 原理: 利用 C 语言字符串以
\0结尾的特性,在传递给底层文件系统函数时截断路径或文件名。 - GET vs POST: GET 请求中的
%00会被自动解码;POST 请求中需在 Burp 等工具的 Hex 视图中手动将%00的 URL 编码 (25 30 30) 改为实际的空字节 (00)。
文件头绕过与内容检测对抗:
- 图片马:
GIF89a+<?php ... ?> - 对抗
getimagesize()/exif_imagetype(): 这些函数通常只检查文件头和少量元数据。只要文件头合法,后面跟代码通常能过。 - 对抗 GD/Imagick (二次渲染): 绕过需要构造能在渲染/压缩后仍保留恶意代码的特殊图片。
- 触发良性错误: 主要用于信息收集和 DoS,不太可能直接 RCE。GD 库(以及 Imagick)在尝试加载和解析图像文件时,如果文件结构严重损坏或不符合规范,对应的
imagecreatefromXXX函数会失败并返回false,同时通常会触发一个 PHP Warning 或 Notice(如果服务器开启了错误显示display_errors或记录日志log_errors)。
- 利用库的 CVE 漏洞: 如果成功,可以在图片被“处理”的瞬间就获得代码执行权限。
- 触发良性错误: 主要用于信息收集和 DoS,不太可能直接 RCE。GD 库(以及 Imagick)在尝试加载和解析图像文件时,如果文件结构严重损坏或不符合规范,对应的
- 图片马:
条件竞争 (Race Condition) 利用:
- 识别: 寻找“先移动/保存,后检查/删除”的代码逻辑。
- 利用: 上传文件后,立即、高并发地访问目标文件 URL。使用多线程脚本(如 Python
requests+threading)提高成功率。 - 变种: 上传“写入器” (Writer Shell),高并发访问写入器,让它在被删除前创建最终的 Webshell。
file_put_contents()vsunlink():- 虽然利用文件句柄阻止
unlink在 Web 场景下很难稳定复现,但可以关注权限问题。如果在file_put_contents后、unlink前,能通过其他漏洞改变文件或目录权限,使 Web 进程无法删除,则可持久化。
- 虽然利用文件句柄阻止
PHP 可解析标签绕过 WAF:
- 如果 WAF 只检测
<?php,而服务器开启了short_open_tag=On,则<? ... ?>可绕过。 <% ... %>和<script language="php">在 PHP 7 已移除,但老系统可能存在。
- 如果 WAF 只检测
命令执行函数选择:
* 需要看回显,且回显简单:system()(直接输出 + 最后一行返回)。
* 需要完整回显,自己处理:exec()(存入数组) 或shell_exec()/反引号 (返回完整字符串)。
* 需要原始/二进制输出:passthru()。
* 安全: 永远用escapeshellarg()处理要传递给命令的参数。上传 + 重命名:
- 先上传一个看似合法的文件(如
shell.php.jpg)绕过上传检查。 - 再利用不安全的“重命名”功能将其改为
shell.php。关键在于重命名功能是否做了和上传时同样严格的后缀检查。
- 先上传一个看似合法的文件(如
三、 其他潜在漏洞结合
- 文件上传成功但无法直接执行时,寻找 LFI (本地文件包含) 漏洞来包含上传的文件(即使是
.jpg后缀的图片马)。 - 寻找 命令注入 (Command Injection) 漏洞,利用它来执行
mv或cp命令,将上传到非 Web 目录的文件移动到 Web 目录下,或者直接执行上传的脚本。 - 关注 SSRF,如果上传功能支持从 URL 拉取文件,可能用于探测内网或读取本地文件。
- 关注 XXE,如果上传的是 XML 或相关格式(DOCX, ODT)且服务器解析不当。
伪造方式
伪造文件名 (Filename Forgery):
- 主要用来绕过基于后缀名的黑名单或白名单检查。
- 怎么做?
- 简单后缀修改: 服务器禁止
.php,但允许.jpg。你在上传shell.php时,通过 Burp Suite 或curl将请求中Content-Disposition里的filename="shell.php"修改为filename="shell.jpg"。 - 利用系统特性:
- Windows 下末尾加点
.:filename="shell.php."可能绕过只检测.php的规则,但最终保存为shell.php。 - Windows 下末尾加空格:
filename="shell.php "(需在 Burp Hex 视图添加20)。 - NTFS 流:
filename="shell.php::$DATA"。
- Windows 下末尾加点
- 大小写混淆:
filename="shell.PhP"(如果服务器检查区分大小写)。 - 双写/特殊字符:
filename="shell.pphphp"(绕过简单替换),shell.php:.jpg(特定 Windows 场景)。
- 简单后缀修改: 服务器禁止
- 目的: 让服务器在检查文件名/后缀时认为你上传的是一个允许的文件类型,即使实际文件内容或你想最终保存的文件名是恶意的。
伪造
Content-Type(MIME Type Forgery):- 有什么用? 绕过基于 HTTP 请求中
Content-Type头部进行的服务器端验证。很多应用会检查这个值是否属于允许的 MIME 类型列表(如image/jpeg,image/png)。 - 怎么做? 在
multipart/form-data请求中,找到对应文件的那一部分,将其Content-Type头部的值修改为服务器允许的类型。例如,上传shell.php时,将其部分的Content-Type从application/octet-stream或application/x-php修改为image/jpeg。 - 目的: 让服务器认为你上传的文件内容类型是合法的,从而通过基于 MIME 类型的检查关卡。
- 有什么用? 绕过基于 HTTP 请求中
伪造文件内容 - 文件头 (Magic Bytes Forgery):
- 有什么用? 绕过基于文件开头的几个“魔术字节”(Magic Bytes)进行的服务器端文件类型验证。服务器可能会读取文件的前几个字节来判断它是不是一个真正的图片、PDF 等。
- 怎么做? 在你的 Webshell 代码(如
<?php ... ?>)前面,手动添加上目标服务器允许的文件类型的合法文件头。- 例如,添加
GIF89a来伪装成 GIF 文件。 - 添加
FF D8 FF E0(或FF D8 FF E1) 来伪装成 JPG 文件。 - 添加
89 50 4E 47 0D 0A 1A 0A来伪装成 PNG 文件。
- 例如,添加
- 目的: 让服务器在读取文件开头进行内容校验时,认为这是一个合法的文件类型,从而通过基于文件头的检查。这通常用于制作“图片马”。
- Title: 文件上传漏洞-学习笔记-2
- Author: KaldX
- Created at : 2025-04-23 20:00:00
- Updated at : 2025-04-23 20:00:00
- Link: https://blog.kaldx.com/2025/04/23/文件上传漏洞-学习笔记-2/
- License: This work is licensed under CC BY-NC-SA 4.0.
Comments