文件上传漏洞基础教程
文件上传漏洞基础教程
概述
什么是文件上传漏洞
文件上传漏洞是Web应用程序中最常见的安全问题之一。当Web应用程序允许用户上传文件而没有进行适当的验证和过滤时,攻击者可以上传恶意文件来执行任意代码、窃取敏感数据或控制服务器。
常见攻击方式
- 上传Webshell文件(PHP、JSP、ASP等)
- 上传配置文件(.htaccess、.user.ini)
- 上传包含代码的恶意图片
- 上传包含恶意内容的压缩文件
基础概念
PHP文件上传机制
在PHP中,文件上传通过$_FILES超全局数组来处理。这个数组包含了上传文件的所有相关信息:
$_FILES['file']['name']:客户端文件的原名称$_FILES['file']['type']:文件的MIME类型(由客户端提供)$_FILES['file']['size']:已上传文件的大小,单位为字节$_FILES['file']['tmp_name']:文件被上传后在服务端储存的临时文件名$_FILES['file']['error']:和该文件上传相关的错误代码
常用上传函数包括:
move_uploaded_file($tmp_name, $destination):将上传的文件移动到新位置is_uploaded_file($filename):检查文件是否通过HTTP POST上传file_exists($path):检查文件或目录是否存在
MIME类型
MIME(Multipurpose Internet Mail Extensions)类型是一种标准,用来表示文档、文件或字节流的性质和格式。常见的MIME类型包括:
image/jpeg:JPEG图像image/png:PNG图像image/gif:GIF图像image/bmp:BMP图像text/html:HTML文件text/plain:纯文本application/x-httpd-php:PHP文件multipart/form-data:表单数据(用于文件上传)
客户端验证绕过
检测方法
客户端验证通常通过JavaScript在前端进行。在上传表单中,JavaScript代码会检查文件扩展名是否在允许的范围内。例如:
1 | function checkFile() { |
绕过方法
方法一:禁用JavaScript
原理:JavaScript验证完全依赖于客户端的JavaScript执行。如果禁用了JavaScript,验证代码就不会执行,文件就可以直接上传。
实现方式:
- 在浏览器设置中禁用JavaScript
- 使用浏览器开发者工具禁用脚本
方法二:修改JavaScript代码
原理:通过拦截服务器响应并修改其中的JavaScript代码,可以删除或绕过验证逻辑。这种方法利用了HTTP响应可以被修改的特性。
实现步骤:
- 打开Burp Suite并拦截请求
- 启用”Do intercept - Response to this request”选项
- 修改响应,删除或修改验证代码
- 转发修改后的响应
方法三:上传前修改文件扩展名
原理:JavaScript只检查文件扩展名,不检查文件实际内容。因此,可以将恶意文件重命名为允许的扩展名上传,然后在传输过程中改回原始扩展名。
实现步骤:
- 将恶意文件重命名为允许的扩展名(如:shell.php → shell.jpg)
- 上传文件
- 使用Burp Suite拦截请求
- 在请求中将扩展名改回.php
实例演示
原始请求:
1 | POST /upload.php HTTP/1.1 |
修改后的请求:
1 | POST /upload.php HTTP/1.1 |
服务器端MIME类型验证绕过
检测方法
服务器端MIME类型验证通过检查HTTP请求头中的Content-Type字段来判断文件类型。例如:
1 | if (($_FILES['upload_file']['type'] == 'image/jpeg') || |
绕过方法
修改Content-Type头
原理:Content-Type字段是由客户端提供的,攻击者可以随意修改这个字段的值。服务器只检查这个字段的值,而不验证文件的实际内容,因此可以通过修改Content-Type来绕过验证。
实现方式:使用Burp Suite拦截请求,将Content-Type从application/x-httpd-php改为image/jpeg。
实例演示
原始请求:
1 | Content-Disposition: form-data; name="file"; filename="shell.php" |
修改后的请求:
1 | Content-Disposition: form-data; name="file"; filename="shell.php" |
黑名单过滤绕过
检测方法
黑名单过滤通过定义不允许的文件扩展名列表来阻止危险文件上传。例如:
1 | $deny_ext = array('.asp','.aspx','.php','.jsp'); |
绕过方法
方法一:使用替代扩展名
原理:黑名单只能包含有限的扩展名,而PHP支持多种扩展名,如.php5、.php4、.phtml等。如果这些扩展名不在黑名单中,就可以绕过过滤。
常见的PHP替代扩展名:
- .php5
- .php4
- .php3
- .php2
- .phtml
- .pht
方法二:大小写变化
原理:PHP的in_array()函数默认是区分大小写的。如果黑名单只包含小写的.php,那么.PHP、.PhP等大小写变体就可以绕过过滤。
常见变体:
- .PHP
- .PhP
- .pHp
- .PHp
方法三:双扩展名
原理:某些服务器配置会根据最后一个扩展名来决定如何处理文件。例如,file.php.jpg可能被当作JPG文件处理,但如果服务器配置不当,PHP代码仍然可能被执行。
常见方式:
- file.php.jpg
- file.php.png
方法四:空字节注入(PHP < 5.3.4)
原理:在PHP 5.3.4之前的版本中,文件名中的空字节(\x00)会被截断。例如,file.php\x00.jpg会被解析为file.php,从而绕过黑名单过滤。
常见方式:
- file.php%00.jpg
- file.php\x00.jpg
方法五:空格和点技巧
原理:Windows文件系统对文件名的处理有特殊规则。末尾的点会被自动删除,空格也可能被忽略。这些特性可以用来绕过黑名单过滤。
常见方式:
file.php.(Windows会自动删除末尾的点)file.php.(末尾有空格和点)file.php::$DATA(Windows NTFS备用数据流特性)
实例演示
上传文件为shell.php5:
1 | // 服务器代码 |
.htaccess文件上传
原理
.htaccess是Apache Web服务器使用的分布式配置文件,它可以更改服务器对特定目录及其所有子目录的配置。通过上传精心构造的.htaccess文件,可以修改服务器的行为,使得其他文件(如图片文件)被当作PHP脚本执行。
检测方法
检查.htaccess是否在黑名单中。如果黑名单没有包含.htaccess,就可以上传这个文件。
绕过步骤
第一步:创建.htaccess文件
文件内容:
1 | <FilesMatch "shell.jpg"> |
这个指令告诉Apache将shell.jpg当作PHP文件执行。
第二步:上传.htaccess
将.htaccess文件上传到上传目录。
第三步:上传恶意图片
创建shell.jpg,包含PHP代码:
1 | @eval($_POST['cmd']); |
上传这个文件,它将被作为PHP执行。
替代配置方式:
- 将目录中的所有文件作为PHP执行:
AddType application/x-httpd-php .jpg - 将特定文件作为PHP执行:
<Files "shell.jpg">SetHandler application/x-httpd-php</Files> - 将所有.jpg文件作为PHP执行:
<FilesMatch "\.jpg$">SetHandler application/x-httpd-php</FilesMatch>
.user.ini文件上传
原理
.user.ini是PHP配置文件,可用于覆盖特定目录的php.ini设置。它适用于PHP-FPM和CGI环境。通过上传.user.ini文件,可以配置PHP在执行任何脚本之前自动包含某个文件,从而实现代码执行。
检测方法
检查.user.ini是否在黑名单中。如果黑名单没有包含.user.ini,就可以上传这个文件。
绕过步骤
第一步:创建.user.ini文件
文件内容:
1 | auto_prepend_file = shell.jpg |
这个指令告诉PHP在执行目录中的任何PHP文件之前自动包含shell.jpg。
第二步:上传.user.ini
将.user.ini文件上传到上传目录。
第三步:上传恶意图片
创建包含PHP代码的shell.jpg并上传。
替代配置方式:
- 在主脚本之后包含文件:
auto_append_file = shell.jpg - 包含多个文件:
auto_prepend_file = shell1.jpg,shell2.jpg
Windows文件命名技巧
末尾的点
原理:Windows文件系统会自动删除文件名末尾的点。例如,file.php.会被保存为file.php。这个特性可以用来绕过黑名单过滤。
实例:上传shell.php.,存储为shell.php
空格
原理:Windows对文件名中的空格处理比较宽松。在某些情况下,空格会被忽略或自动删除。这个特性也可以用来绕过某些过滤机制。
实例:上传shell.php.,处理后可能变为shell.php
::$DATA流
原理:Windows NTFS文件系统支持备用数据流(Alternate Data Streams,ADS)。通过使用::$DATA后缀,可以访问文件的主数据流。例如,file.php::$DATA会被解析为file.php。这个特性可以绕过黑名单过滤。
实例:上传shell.php::$DATA,存储为shell.php
基础Webshell
简单Webshell
1 |
|
密码保护的Webshell
1 |
|
多功能Webshell
1 |
|
测试工具
Burp Suite
Burp Suite是一个用于Web应用安全测试的集成平台。主要功能包括:
- 拦截和修改HTTP请求
- Repeater用于测试不同的payload
- Intruder用于自动化攻击
蚁剑(AntSword)
蚁剑是一个开源的webshell管理工具,主要特点:
- 支持多种连接方式
- 文件管理和终端访问
- 插件系统扩展功能
Metasploit
Metasploit是一个渗透测试框架,包含多个文件上传相关的exploit模块:
1 | use exploit/multi/http/php_upload |
防护措施
服务器端验证
白名单方法
原理:白名单只允许特定的文件扩展名,比黑名单更安全。因为黑名单总是可能遗漏某些扩展名,而白名单只允许已知安全的扩展名。
1 | $allow_ext = array('.jpg', '.jpeg', '.png', '.gif'); |
文件内容验证
原理:通过检查文件的MIME类型,可以验证文件的实际内容是否与扩展名匹配。这可以防止攻击者简单地修改文件扩展名。
1 | $finfo = new finfo(FILEINFO_MIME_TYPE); |
图像验证
原理:使用getimagesize()函数可以验证文件是否为有效的图像文件。这个函数会读取图像文件头,如果文件不是有效的图像,会返回false。
1 | $image_info = getimagesize($_FILES['file']['tmp_name']); |
文件重命名
原理:将上传的文件重命名为随机生成的名称,可以防止攻击者猜测文件路径,并避免文件名冲突。
1 | $new_filename = md5(uniqid()) . '.jpg'; |
存储在Web根目录之外
原理:将上传的文件存储在Web根目录之外,可以防止直接通过HTTP访问这些文件。需要通过单独的脚本来提供文件访问。
1 | $upload_dir = '/var/www/uploads/'; // 在Web根目录之外 |
禁用脚本执行
原理:通过配置Web服务器,在上传目录中禁用PHP脚本的执行,可以防止上传的恶意文件被执行。
1 | # 在上传目录中的.htaccess |
总结
本教程涵盖了文件上传漏洞的基础知识,包括:
- 客户端验证绕过
- 服务器端MIME类型验证绕过
- 黑名单过滤绕过
- .htaccess和.user.ini攻击
- Windows文件命名技巧
这些技术构成了理解更高级文件上传攻击的基础,将在下一篇文章中继续介绍。


