DVWA High级别文件上传漏洞:绕过图片校验获取Webshell的攻防实战
2026/6/19 8:52:58 网站建设 项目流程

1. 项目概述:从文件上传到Webshell的攻防博弈

在Web安全的学习与实践中,文件上传漏洞因其直接、高效的特点,始终是攻击者获取服务器权限的“黄金入口”。今天,我想以一个经典的靶场环境——DVWA(Damn Vulnerable Web Application)的High级别文件上传关卡为例,深入拆解一次完整的攻击链。这不仅仅是上传一个PHP文件那么简单,High级别的防护机制引入了文件内容校验,将我们带入了更深层次的攻防对抗。通过这个案例,你将清晰地理解,当简单的扩展名黑名单、MIME类型校验失效后,攻击者如何利用服务器对文件解析的“信任”,以及如何结合其他漏洞(如文件包含)来最终达成目标——获取一个可交互的Webshell。对于安全研究人员、渗透测试工程师和开发者而言,理解这个过程的每一步,其价值远大于记住几个Payload。它关乎于对安全机制本质的思考:防御为何失效,以及攻击如何绕过层层设防。

2. 环境准备与核心思路解析

2.1 DVWA靶场环境搭建要点

要复现这个漏洞,首先需要一个可控的测试环境。DVWA通常部署在集成环境如XAMPP、PHPStudy或直接运行于Docker中。这里有几个关键点需要注意,它们直接影响后续攻击的成功率:

  1. PHP版本与配置:DVWA的High级别文件上传漏洞利用,依赖于PHP的某些特性。建议使用PHP 5.x 或 7.x 版本,并确保allow_url_includeallow_url_fopen配置在php.ini中根据测试需求调整(对于文件包含利用环节)。在实际靶场搭建时,我习惯使用Docker,因为它能快速重置环境,避免残留文件干扰测试。命令类似docker run --rm -it -p 80:80 vulnerables/web-dvwa即可快速启动一个标准环境。

  2. 安全级别设置:登录DVWA后,务必在左侧“DVWA Security”选项卡中将安全级别调整为“High”。这个操作会改变所有漏洞模块的源代码,启用更严格的防护。我们即将分析的,正是High级别下的文件上传逻辑。

  3. 工具准备:本次演示主要需要三样工具:一个浏览器(用于访问DVWA)、一个文本编辑器(用于制作Payload)、以及一个代理工具Burp Suite。Burp Suite并非必须,但对于理解HTTP请求的细节和进行手动修改至关重要。当然,你也可以使用浏览器开发者工具的网络选项卡,但Burp的功能更强大、更直观。

2.2 High级别防护机制深度剖析

在开始攻击前,我们必须像代码审计员一样,仔细阅读High级别的源代码。这是制定有效攻击策略的基础。从提供的源码片段中,我们可以提炼出High级别的三重防御:

  1. 扩展名白名单:代码通过strtolower( $uploaded_ext )提取并小写化文件扩展名,只允许jpgjpegpng。这比Medium级别检查Content-Type更为严格,因为扩展名是从用户上传的文件名中解析的,直接修改HTTP包中的MIME类型已无法绕过。

  2. 文件大小限制$uploaded_size < 100000,即文件需小于100KB。这主要是为了防止通过上传超大文件进行DoS攻击,对我们上传小体积的Webshell影响不大。

  3. 文件内容校验:这是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.phpshell.jpg%00.php(空字节截断,在特定PHP版本下)等。但在DVWA High级别中,它严格检查扩展名,这种解析漏洞的利用条件通常不成立。

因此,路径一“图片马”成为我们的主攻方向。但仅仅上传成功图片马还不够,因为服务器默认会以图片的MIME类型(如image/jpeg)来响应这个文件,浏览器会尝试显示它,而不会执行其中的PHP代码。所以,我们需要第二步:找到一个方式,让服务器“主动地”以PHP方式来解析这个图片文件。在DVWA环境中,通常借助文件包含漏洞(File Inclusion)来实现。通过文件包含漏洞,我们可以使用php://包装器或者file://协议(需allow_url_include=On)去包含我们上传的图片马,此时包含文件的代码会被当作PHP代码执行。

总结攻击链:制作图片马 -> 利用High级别上传逻辑的“缺陷”(只校验文件头)上传成功 -> 结合文件包含漏洞触发PHP代码执行 -> 获取Webshell。

3. 制作与上传图片Webshell

3.1 手工打造一个“合格”的图片马

图片马的本质是在不破坏原有图片文件结构的前提下,将PHP代码附加到文件末尾。对于JPEG格式,其文件结束标记是FF D9。在这之后添加的任何数据,都会被标准的图片解析器忽略。我们可以利用这个特性。

实操步骤:

  1. 准备素材

    • 一张普通的JPEG图片,例如cat.jpg。尽量选择小尺寸的,以满足100KB的大小限制。
    • 一个PHP一句话木马,内容为:<?php @eval($_POST[‘cmd’]);?>。将其保存为shell.php。这里使用@符号是为了抑制可能产生的错误信息,使其更隐蔽。cmd是我们后续连接时使用的参数名。
  2. 使用系统命令进行合并(Windows环境): 打开命令提示符(CMD),使用copy命令的二进制合并功能:

    copy /b cat.jpg + shell.php cat_shell.jpg

    /b参数表示以二进制模式进行复制。这条命令会将cat.jpg的所有字节,紧接着shell.php的所有字节,顺序写入到新文件cat_shell.jpg中。

  3. 验证文件

    • 用图片查看器打开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图片并返回尺寸等信息。

  1. 访问DVWA,将安全级别调至High,进入“File Upload”模块。
  2. 选择我们制作的cat_shell.jpg文件,点击“Upload”。
  3. 如果一切顺利,页面会显示“.../hackable/uploads/cat_shell.jpgsuccessfully uploaded!”。

成功的关键:我们完全遵守了服务器端的校验规则:扩展名是.jpg,文件头是合法的JPEG。服务器“看到”的,就是一个普通的图片文件。我们的恶意代码,安静地躺在文件末尾,等待着被唤醒的机会。

4. 利用文件包含漏洞激活Webshell

4.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/?page=include.php后端代码可能类似于:include($_GET[‘page’]);这意味着我们可以控制page参数的值,让服务器去包含任何(在允许路径内的)文件。

4.2 构造利用Payload执行图片马中的代码

我们的目标是让服务器通过文件包含功能,去包含我们上传的图片马cat_shell.jpg,并希望PHP引擎能执行其中的代码。这里有两种常见的利用协议:

  • php://协议结合php://inputphp://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中解码并执行它。

构造Payload

http://dvwa/vulnerabilities/fi/?page=php://filter/read=convert.base64-encode/resource=file:///var/www/html/dvwa/hackable/uploads/cat_shell.jpg

注意:上面的路径/var/www/html/dvwa/是Linux下常见的路径,请根据你的实际DVWA安装路径进行调整。Windows路径可能是file:///D:/phpstudy/WWW/dvwa/...

这个Payload做了以下几件事:

  1. resource=file:///...:指定要读取的资源是我们上传的图片马。
  2. read=convert.base64-encode:将读取到的原始字节(包括JPEG头和我们的一句话木马)全部进行Base64编码。
  3. 整个php://filter流的内容就是这个图片马的Base64编码字符串。
  4. 当这个字符串被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_gpc=off,且包含时不对内容做严格检查),服务器可能会尝试解析整个文件,当扫描到<?php标签时就开始执行。但这并不可靠。

更可靠的实战方法:使用Webshell连接工具直接连接在真实渗透测试或更可控的靶场中,上传图片马后,最常用的方法是直接使用中国蚁剑(AntSword)或中国菜刀(Caidao)这类Webshell管理工具进行连接。你需要知道图片马上传后的完整URL,例如http://dvwa/hackable/uploads/cat_shell.jpg。在蚁剑中添加数据时,URL地址就填这个,连接密码填你的一句话木马中定义的POST参数名,例如cmd

为什么这样可以?因为蚁剑在连接时,会向这个URL发送一个特殊的HTTP POST请求,请求体中包含了经过加密的指令(如cmd=system(‘whoami’);)。我们的图片马被访问时,虽然服务器默认把它当图片,但PHP引擎仍然会处理这个请求。当cat_shell.jpg被请求时,Web服务器(如Apache)会根据其配置决定如何处理.jpg文件。如果服务器配置了AddType application/x-httpd-php .jpg(这是一种危险配置),那么.jpg文件也会被PHP解析器处理。在这种情况下,请求发送到cat_shell.jpg,PHP引擎解析该文件,遇到eval($_POST[‘cmd’]),就会执行蚁剑POST过来的指令。

在DVWA默认配置中,通常不会将.jpg映射给PHP解析器。因此,在纯粹的DVWA High文件上传挑战中,标准的、预期的解法往往是结合文件包含漏洞。你需要先上传图片马,然后利用File Inclusion模块的Low级别漏洞,通过php://filter等复杂技巧去包含并执行它,或者寻找其他途径(如日志注入、环境变量控制等)来执行代码。

5. 漏洞防御的深层思考与加固建议

通过这次对High级别文件上传漏洞的攻防演示,我们可以看到,即使采用了扩展名白名单和文件内容校验,攻击者依然有机会通过制作图片马的方式绕过前端防御。真正的安全需要多层次、纵深的防御。

5.1 从Impossible级别学习最佳实践

DVWA的Impossible级别为我们提供了近乎完美的防御范例:

  1. Anti-CSRF Token:在表单中添加随机Token,防止CSRF攻击重复提交恶意文件。
  2. 文件重命名:使用md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext为上传文件生成一个随机的、不可预测的文件名,防止攻击者直接访问或猜测文件路径。
  3. 二次渲染(图像重建):这是最关键的一步。代码使用imagecreatefromjpeg/imagecreatefrompngimagejpeg/imagepng函数,将上传的图片在服务器端重新生成了一遍。这个过程会剥离所有非图像的元数据和附加在文件末尾的任何数据。我们的图片马在经历二次渲染后,末尾的PHP代码会被彻底清除,只留下一张“干净”的图片。
  4. 多维度校验:同时检查扩展名、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级别漏洞的拆解,能让你不仅学会一次攻击,更能洞察背后完整的安全逻辑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询