1. 项目概述:为什么我们需要Upload-labs?
如果你是一名Web安全爱好者、渗透测试工程师,或者是一名正在学习网络安全的学生,那么“文件上传漏洞”这个词对你来说一定不陌生。它几乎是Web应用中最常见、也最危险的漏洞类型之一。一个看似无害的图片上传功能,如果存在缺陷,就可能成为攻击者直通服务器后门的“高速公路”,导致网站被完全控制、数据泄露,甚至服务器沦为“肉鸡”。
然而,理论学习总是隔靴搔痒。看再多的漏洞原理文章,也不如亲手“黑”掉一个靶场来得深刻。这就是Upload-labs存在的意义。它不是一个真实存在漏洞的网站,而是一个由安全研究者c0ny1精心设计的、专门用于学习和研究文件上传漏洞的PHP靶场。它模拟了从最基础的客户端校验,到复杂的服务器端逻辑缺陷、条件竞争等21种(甚至更多)常见的文件上传漏洞场景。
通过这个靶场,你可以:
- 系统性学习:从易到难,逐一攻破不同类型的防御机制,构建完整的文件上传漏洞知识体系。
- 实战化练习:在安全、合法的环境中,使用Burp Suite、蚁剑等真实渗透测试工具进行攻击演练。
- 理解防御逻辑:每一关都对应一种或多种常见的防御手段(如黑名单、白名单、MIME类型检查、内容检测等),在尝试绕过它们的过程中,你才能真正理解这些防御措施的局限性和绕过思路。
- 培养审计思维:很多关卡需要你阅读并理解后端PHP源码(靶场提供),从而找到逻辑缺陷。这能极大地锻炼你的代码审计能力。
简单来说,通关Upload-labs,就相当于完成了一次针对文件上传漏洞的“红队”专项训练。接下来,我将带你从环境搭建开始,手把手复现这些经典的漏洞场景,并分享我在通关过程中积累的实战技巧和避坑指南。
2. 靶场环境搭建与基础工具准备
工欲善其事,必先利其器。在开始“闯关”之前,我们需要一个能运行PHP的Web服务器环境,以及几件趁手的“兵器”。
2.1 本地环境搭建(以Windows + PHPStudy为例)
对于绝大多数学习者来说,在本地Windows系统上使用PHPStudy集成环境是最快捷、稳定的选择。
步骤一:下载与部署Upload-labs
- 访问Upload-labs的GitHub仓库(
https://github.com/c0ny1/upload-labs),点击“Code” -> “Download ZIP”下载源码压缩包。 - 解压下载的ZIP文件,你会得到一个名为
upload-labs-master的文件夹。 - 将其重命名为
upload-labs(方便访问),然后整个文件夹复制到你的PHPStudy安装目录下的WWW目录中。例如,我的路径是D:\phpstudy_pro\WWW\upload-labs。
步骤二:配置PHPStudy
- 启动PHPStudy,在“网站”页面点击“创建网站”。
- 域名填写一个你容易记住的,比如
www.upload-labs.com。 - 端口保持80(如果80端口被占用,可改为81、8080等)。
- 根目录选择你刚才复制进去的
upload-labs文件夹。 - PHP版本选择5.x系列(非常重要!因为部分关卡,如%00截断,依赖于PHP版本<5.3且
magic_quotes_gpc关闭的特性,PHPStudy的5.x版本默认配置更贴近漏洞环境)。这里我选择PHP-5.6.9。 - 点击“确认”保存。
步骤三:访问与初始化
- 在浏览器地址栏输入你设置的域名,如
http://www.upload-labs.com。 - 如果一切正常,你将看到Upload-labs的首页,上面列出了所有关卡。点击“安装/重置数据库”按钮,初始化靶场环境。
- 现在,你可以点击任意一关(如Pass-01)开始挑战了。
注意:如果你在后续关卡(如Pass-16)遇到图片检测函数
exif_imagetype()报错,提示函数未定义,需要开启php_exif扩展。在PHPStudy的“软件管理”中找到对应PHP版本的“设置”->“php扩展”,勾选php_exif并重启服务即可。
2.2 核心工具准备
一个合格的“黑客”,离不开他的工具包。以下是通关Upload-labs必备的几样工具:
Burp Suite Community(抓包与改包神器):这是我们的主力工具。用于拦截浏览器发送的HTTP请求,修改文件名、Content-Type、参数等,以绕过前端和后端的各种检查。
- 使用要点:浏览器需要配置代理(通常为127.0.0.1:8080),并安装Burp Suite签发的CA证书(用于拦截HTTPS流量)。在“Proxy”->“Intercept”选项卡中,确保“Intercept is on”是打开状态。
中国蚁剑/AntSword(Webshell管理工具):当我们成功上传一个包含恶意代码(如一句话木马)的文件后,需要使用蚁剑这样的工具来连接和管理这个“后门”,从而验证漏洞利用是否成功。
- 使用要点:确保你的杀毒软件不会误杀它。添加数据时,URL地址就是你上传的Webshell文件访问地址,连接密码是Webshell文件中设定的POST参数值(如
$_POST[‘pass’]中的pass)。
- 使用要点:确保你的杀毒软件不会误杀它。添加数据时,URL地址就是你上传的Webshell文件访问地址,连接密码是Webshell文件中设定的POST参数值(如
Notepad++ / VS Code(代码与文本编辑器):用于编写和修改Webshell代码、制作图片马、查看文件十六进制内容等。
- 使用要点:对于制作图片马,推荐使用具备十六进制编辑功能的插件或独立工具,如WinHex或010 Editor,但Notepad++配合特定插件也能完成基础操作。
浏览器开发者工具(F12):主要用于第一关的JS绕过。通过查看网页源码、禁用JavaScript等方式,快速定位前端校验逻辑。
实操心得:建议在虚拟机(如VMware或VirtualBox)中搭建整个环境。这样即使操作失误导致系统异常,也可以快速恢复快照,避免影响宿主机。同时,虚拟机环境也更贴近真实的隔离测试环境。
3. 核心漏洞场景复现与深度解析
我们将Upload-labs的关卡按照防御机制的演进和绕过技术的难度,分为几个阶段进行攻克。每个阶段不仅会演示如何绕过,更会深入剖析其背后的原理和代码逻辑。
3.1 第一阶段:前端与基础校验绕过(Pass-01 ~ Pass-03)
这个阶段的防御手段相对初级,主要集中于客户端的校验和简单的服务器端黑名单。
3.1.1 Pass-01:客户端JavaScript校验绕过
- 漏洞场景:网站通过嵌入在HTML页面中的JavaScript代码,在上传前检查文件后缀名。如果不符合要求(如不是.jpg/.png等图片后缀),则弹出警告并阻止表单提交。
- 源码审计:查看网页源代码,你会发现类似下面的JS函数:
function checkFile() { var file = document.getElementById("upload_file").value; if (file == null || file == "") { alert("请选择要上传的文件!"); return false; } // 检查后缀名 var allow_ext = ".jpg|.png|.gif"; var ext_name = file.substring(file.lastIndexOf(".")); if (allow_ext.indexOf(ext_name) == -1) { alert("该文件不允许上传,请上传" + allow_ext + "类型的文件!"); return false; } } - 攻击与绕过:这种校验完全在用户的浏览器上运行,攻击者可以完全控制客户端环境。
- 直接禁用JS:在浏览器设置中禁用JavaScript(如Firefox的
about:config页面,将javascript.enabled设为false),然后刷新页面再上传.php文件。 - 抓包修改:即使不禁用JS,也可以先选择一个合法的图片文件(如
shell.jpg),在Burp Suite拦截到的上传请求中,将文件名直接改为shell.php。 - 删除前端代码:使用浏览器开发者工具(F12)直接删除或修改页面的JS校验函数。
- 直接禁用JS:在浏览器设置中禁用JavaScript(如Firefox的
- 核心原理:“永远不要信任客户端”。任何前端校验都只能提升用户体验,绝不能作为安全防御手段。安全校验必须在服务器端进行。
3.1.2 Pass-02:MIME类型(Content-Type)校验绕过
- 漏洞场景:服务器端检查HTTP请求头中的
Content-Type字段。该字段由浏览器根据文件后缀自动生成(如.jpg对应image/jpeg)。后端代码只允许image/jpeg,image/png,image/gif等类型。 - 源码审计:后端PHP代码关键部分:
$type = $_FILES['upload_file']['type']; $allow_type = array('image/jpeg','image/png','image/gif'); if(!in_array($type, $allow_type)){ // 不允许上传 } - 攻击与绕过:使用Burp Suite拦截上传请求,直接修改
Content-Type头为允许的值(如image/jpeg),即可绕过。 - 核心原理:
Content-Type同样来自客户端请求,可以被轻易篡改。它不能作为判断文件真实类型的依据。
3.1.3 Pass-03:黑名单遗漏特殊可解析后缀
- 漏洞场景:服务器端采用了黑名单机制,禁止了
.asp,.aspx,.php,.jsp等常见脚本后缀。但黑名单并不完整。 - 源码审计:黑名单数组可能如下:
$deny_ext = array('asp','aspx','php','jsp'); - 攻击与绕过:寻找未被列入黑名单但服务器仍会解析的后缀。在Apache+PHP环境中,除了
.php,还可能解析.php3,.php4,.php5,.phtml,.phps等,具体取决于httpd.conf或.htaccess中的配置。例如,如果服务器配置了AddType application/x-php .php .php5 .phtml,那么上传.php5后缀的文件同样会被当作PHP执行。 - 实操要点:在实战中,信息收集至关重要。你需要探测目标服务器支持解析哪些后缀。方法包括:查看报错信息、扫描目录下的
phpinfo.php文件、或尝试上传各种后缀的测试文件观察行为。
注意事项:从这一关开始,我们上传的“Webshell”内容需要调整。为了避免一句话木马被简单的字符串检测发现,可以使用更隐蔽的写法,如
<?php eval($_REQUEST[‘a’]);?>,或者使用字符串变形、编码等方式。在靶场中,我们可以先用<?php phpinfo();?>来测试文件是否被成功解析,这是最直观的验证方式。
3.2 第二阶段:黑名单绕过技巧进阶(Pass-04 ~ Pass-11)
这一阶段的防御开始关注黑名单的完善和文件名处理逻辑,但依然存在可被利用的缺陷。
3.2.1 Pass-04:.htaccess文件攻击
- 漏洞场景:黑名单非常全面,几乎涵盖了所有已知的可执行脚本后缀。但忽略了
.htaccess这个特殊的配置文件。 - 核心原理:
.htaccess是Apache服务器的分布式配置文件,可以覆盖其所在目录及子目录的服务器配置。通过上传一个恶意的.htaccess文件,我们可以“教会”Apache将特定文件(如图片)当作PHP脚本来解析。 - 攻击步骤:
- 制作.htaccess文件:内容如下,意为将文件名中包含“shell”的文件,都交由PHP解析器处理。
<FilesMatch "shell"> SetHandler application/x-httpd-php </FilesMatch> - 上传.htaccess文件:由于
.htaccess通常不在黑名单内,可以成功上传。 - 上传图片马:上传一个内容为PHP代码(如
<?php phpinfo();?>),但文件名包含“shell”的图片文件,例如shell.jpg。 - 访问图片:访问
http://target/upload/shell.jpg,你会发现服务器执行了其中的PHP代码,而不是显示图片。
- 制作.htaccess文件:内容如下,意为将文件名中包含“shell”的文件,都交由PHP解析器处理。
- 限制与实战意义:此漏洞需要目标服务器是Apache,并且允许
.htaccess覆盖配置(AllowOverride All),同时PHP版本需低于5.3.0(高版本默认安全设置会阻止)。实战中条件较为苛刻,但一旦满足,危害极大。
3.2.2 Pass-05 ~ Pass-08:文件名处理函数缺陷
这几关共同的特点是:后端对上传的文件名进行了一系列“净化”处理(如去除末尾的点、空格、::$DATA等),但处理顺序或完整性存在缺陷。
- Pass-05(后缀加点空格):代码先调用
deldot($file_name)去除文件名末尾的点,再取后缀名。如果我们上传shell.php. .(点+空格+点),经过deldot处理后变成shell.php.(点+空格),取后缀得到.,不在黑名单中。但Windows系统在保存文件时,会自动去除末尾的点和空格,最终文件名为shell.php。 - Pass-06(后缀大小写):黑名单检查是大小写敏感的(如
‘php’),但服务器(Windows系统)对文件后缀名不区分大小写。上传shell.PHP或shell.Php即可绕过。 - Pass-07(后缀加空格):代码去除了字符串
::$DATA,但没有去除空格。上传shell.php(后缀后有一个空格),Burp Suite中需在文件名后直接加空格(显示为shell.php或shell.php%20)。 - Pass-08(后缀加点):代码去除了
::$DATA和空格,但没有去除末尾的点。上传shell.php.即可。Windows系统保存时会去掉最后的点。 - Pass-09(::$DATA流绕过):这是Windows NTFS文件系统的一个特性。
::$DATA是NTFS文件流的数据属性。当文件名为shell.php::$DATA时,系统实际创建的文件是shell.php。代码如果只检查了shell.php::$DATA的后缀(可能是::$DATA或空),就会误判。
通用技巧:在Burp Suite的Repeater或Intruder模块中修改filename参数时,可以直接输入这些特殊字符。对于空格和点,有时需要切换到“Hex”视图,在对应位置修改十六进制值(空格是20,点是2e)来确保准确。
3.2.3 Pass-10(点空格点二次利用)与 Pass-11(双写后缀绕过)
- Pass-10:可以看作是Pass-05的变种或组合利用。当简单的点空格点被防御后,可以尝试其他组合或利用Windows特性。
- Pass-11(双写后缀):后端使用
str_ireplace()函数,将黑名单中的后缀(如php)替换为空字符串。如果我们上传的文件名是shell.pphphp,该函数会从左到右查找并替换。第一次找到php并替换为空,字符串变为shell.pphp。由于替换是循环或全局的,它会继续在结果中查找,又找到php并替换,最终得到shell.php,成功绕过。- Burp Suite操作:将
filename改为shell.pphphp。
- Burp Suite操作:将
3.3 第三阶段:白名单、截断与内容检测(Pass-12 ~ Pass-18)
防御升级到白名单机制和文件内容检测,攻击手段也需要更加精细。
3.3.1 Pass-12 & Pass-13:%00截断攻击
这是非常经典的一种攻击手法,但对环境有严格要求:PHP版本 < 5.3.4,且magic_quotes_gpc配置为off(PHPStudy 5.x默认关闭)。
- 漏洞原理:
%00是URL编码,代表空字符(NULL)。在C语言等底层语言中,空字符是字符串的结束符。PHP在某些旧版本和特定函数中,如果处理用户输入不当,会将%00之后的字符截断。 - 攻击场景(Pass-12, GET型):
- 前端页面上传时,可能有一个隐藏的输入框或参数
save_path,其值可能是../upload/。 - 后端代码拼接完整路径:
$img_path = $_GET[‘save_path’].”/”.$file_name; - 攻击者抓包,将
save_path参数修改为../upload/shell.php%00。同时,上传的文件名可以是任意白名单内的名字,如test.jpg。 - 后端代码拼接后得到:
../upload/shell.php%00/test.jpg。当PHP在处理这个路径字符串时,遇到%00(解码后为空字符)就认为字符串结束,实际保存路径变成了../upload/shell.php。test.jpg的内容就被写入到了shell.php文件中。
- 前端页面上传时,可能有一个隐藏的输入框或参数
- 攻击场景(Pass-13, POST型):与GET型原理相同,但
%00在POST数据中不会自动URL解码。因此需要在Burp Suite中,先输入%00,然后选中这三个字符,右键选择“Convert Selection” -> “URL” -> “URL-decode”,将其解码成一个不可见的空字符(在Hex视图里显示为00)。 - 实战意义:此漏洞在如今的高版本PHP环境中已基本绝迹,但作为历史经典漏洞,理解其原理对于学习安全开发中的“输入验证与净化”至关重要。
3.3.2 Pass-14 ~ Pass-16:文件内容检测与图片马
防御方意识到只检查后缀名不够,开始检查文件的实际内容,通常是检查文件头(Magic Bytes)。
- 漏洞原理:每种文件格式在文件开头都有固定的几个字节标识其类型,例如:
- JPEG:
FF D8 FF E0 - PNG:
89 50 4E 47 - GIF:
47 49 46 38(GIF8) 后端使用getimagesize()、exif_imagetype()等函数读取文件头进行验证。
- JPEG:
- 攻击方法——制作图片马:
- 简单拼接法(Pass-14):在一个正常的图片文件(如
1.jpg)末尾,直接追加PHP代码。使用Windows命令:copy /b 1.jpg + shell.php webshell.jpg。这样生成的文件,文件头是合法的图片,能通过检测,但服务器在解析时,如果遇到文件包含等漏洞,可能会执行末尾的代码。 - 十六进制编辑法(更可靠):使用Notepad++的Hex Editor插件或WinHex工具,打开一个真图片,在不破坏文件头和数据块结构的前提下,找到一块可以插入数据的地方(如图片的注释区),将PHP代码的十六进制值写入。这种方法制作的图片马更隐蔽,不易被破坏。
- 简单拼接法(Pass-14):在一个正常的图片文件(如
- 利用条件:单独的图片马上传成功,并不意味着代码能执行。需要配合文件包含漏洞(Local File Inclusion, LFI)。例如,网站有一个页面
index.php?page=xxx,其中page参数可控,并且服务器未正确过滤,导致可以包含上传的图片马文件,从而执行其中的PHP代码。 - 靶场操作:Upload-labs的这几关通常需要你同时开启文件包含漏洞的测试页面(靶场可能自带或需要你模拟)。你需要先上传图片马,然后通过文件包含漏洞的页面去包含这个图片马文件的路径。
3.3.3 Pass-17:二次渲染绕过
这是图片马攻防的“终极挑战”之一。服务器不仅检查文件头,还会对上传的图片进行“二次渲染”(Re-sample/Re-compress),即用GD库或ImageMagick等库重新生成一张新的图片。这个过程会丢弃所有非图片数据,导致我们追加的恶意代码被清除。
- 攻击思路:寻找二次渲染后保持不变的数据区。对于GIF图片,由于其由多帧组成,在帧与帧之间的部分数据,或者某些注释块(Application Extension),在简单的重新渲染中可能被保留。对于PNG,可以在IDAT数据块之后、IEND数据块之前插入数据。
- 实操步骤(以GIF为例):
- 准备一个正常的GIF图片(
1.gif)和一个Webshell文件(shell.php,内容为<?php phpinfo();?>)。 - 使用命令
copy /b 1.gif + shell.php test.gif制作初级图片马。 - 将
test.gif上传到靶场。上传后,下载服务器处理后的新图片(如upload/xxxx.gif)。 - 使用WinHex同时打开原始的
test.gif和服务器生成的xxxx.gif,进行二进制对比。 - 仔细寻找两块区域中完全相同的十六进制代码段(这些就是未被渲染改变的部分)。通常可以在文件末尾附近找到。
- 将
shell.php中PHP代码的十六进制值,覆盖到原始test.gif中那个“安全区域”对应的字节上(注意是覆盖,不是插入,以免改变文件大小和结构)。 - 保存修改后的GIF文件并再次上传。这次,恶意代码所在的区域在二次渲染中没有被改动,得以保留。
- 利用文件包含漏洞包含这个新的GIF文件,成功执行
phpinfo()。
- 准备一个正常的GIF图片(
- 技术难点:这个过程需要耐心和一定的二进制分析能力。不同图片类型、不同渲染库和参数,保留的数据区都不同,没有通用位置。
3.3.4 Pass-18:条件竞争漏洞
这是一种基于时间的攻击(Time-of-Check to Time-of-Use, TOCTOU)。漏洞逻辑是:服务器先将上传的文件保存到临时目录或公开目录,然后再去检查这个文件是否合法(如检查后缀、内容)。如果检查不通过,就删除它。
- 攻击窗口:在文件被保存之后、被删除之前,存在一个极短的时间窗口。
- 攻击方法:利用多线程/多进程并发请求,一边不断上传Webshell,一边疯狂访问这个Webshell的预期路径。只要在删除动作发生前成功访问一次,Webshell就会被执行,攻击者就能立即写入一个更持久的后门。
- Burp Suite实战(Intruder爆破):
- 准备Payload:上传一个内容为
<?php file_put_contents(‘shell.php’, ‘<?php @eval($_POST[cmd]);?>’);?>的临时Webshell。这个脚本的作用是,一旦被访问,就在当前目录生成一个真正的、持久的Webshell文件shell.php。 - 拦截上传请求:将包含上述代码的文件(如
race.php)上传,Burp Suite拦截请求。 - 发送到Intruder:将拦截到的请求发送到Intruder模块。
- 配置攻击:在“Positions”选项卡,清空所有Payload标记。我们不需要替换任何参数,只需要重复发送这个上传请求。在“Payloads”选项卡,选择“Payload type”为“Null payloads”。在“Payload Options”中,设置“Generate”一个较大的数字,如5000,表示连续发送5000次请求。
- 启动攻击线程:切换到“Options”选项卡,找到“Request Engine”,将“Number of threads”设置为一个较高的值,如50。这代表同时有50个线程在并发上传。
- 启动访问线程:同时,再开一个Burp Suite的Intruder窗口,针对我们上传文件的预期访问地址(如
http://target/upload/race.php)进行同样的高并发访问攻击。 - 观察结果:在两个Intruder窗口都开始攻击后,观察访问请求的响应。如果某个请求的响应长度突然变大(可能包含了生成的
shell.php的内容),或者状态码不同,说明在那个瞬间,race.php被成功执行,持久化Webshell已经写入。立即停止攻击,尝试连接shell.php。
- 准备Payload:上传一个内容为
注意事项:条件竞争攻击的成功率受网络速度、服务器性能影响很大。在本地环境可能很容易成功,但在真实的网络环境中可能需要更精巧的脚本和更持久的尝试。这种漏洞的发现通常需要代码审计,看到“先保存后检查”的逻辑就要警惕。
3.4 第四阶段:综合与逻辑漏洞(Pass-19 ~ Pass-21)
最后几关通常涉及更复杂的逻辑判断、数组操作或配置错误。
3.4.1 Pass-19:.7z后缀与解析特性
- 漏洞场景:白名单机制,但名单中包含了
.7z。后端代码存在路径拼接错误。 - 绕过原理:
- .7z的解析特性:
.7z是压缩文件格式,Web服务器(如Apache)默认不会将其作为脚本解析。当浏览器请求一个.7z文件时,服务器会将其作为二进制流下载。但是,如果文件名为shell.php.7z,一些老旧或有特殊配置的服务器在解析时,可能会尝试从右向左寻找它能处理的扩展名。当它不认识.7z时,会向前寻找,发现.php,于是将其作为PHP文件解析。这是一种不太常见但确实存在的解析漏洞,与Apache的mod_negotiation或多重扩展名解析有关。 - 路径拼接错误:代码中
cls_upload_dir的值本应为$dir.’/’,但错误地写成了$dir,导致最终保存路径错误,文件可能被保存到非预期目录,结合其他漏洞可能造成利用。
- .7z的解析特性:
- 攻击方法:直接上传
shell.php.7z文件,并利用条件竞争(因为本关也是先保存后检查)或结合其他漏洞进行访问。
3.4.2 Pass-20:白名单绕过与逻辑混淆
- 漏洞场景:使用白名单,但校验逻辑存在缺陷。例如,校验时使用了
strtolower()将后缀转为小写再比对白名单(白名单为小写),但在最终保存时,却使用了原始的后缀名。 - 攻击方法:上传
shell.PHP(大写后缀)。校验时,strtolower(‘.PHP’)得到.php,在白名单内,通过。保存时,文件名仍是shell.PHP,而Windows系统不区分大小写,该文件会被当作PHP脚本执行。
3.4.3 Pass-21:数组绕过与逻辑缺陷
这是非常精彩的一关,考察了对PHP数组函数和逻辑的深入理解。
- 漏洞代码逻辑简化:
- 后端通过
$_POST[‘save_name’]接收文件名。它期望是一个字符串,如“shell.jpg”。 - 代码首先检查
$_FILES[‘upload_file’][‘type’]是否为图片MIME类型。 - 然后,它检查
$_POST[‘save_name’]是否为数组:is_array($save_name)。 - 关键点:如果
$save_name是数组,它就不执行后续的按.分割文件名、取后缀校验的逻辑,而是直接跳到数组处理部分。 - 数组处理部分,它用
end($save_name)取数组最后一个元素作为后缀进行白名单校验。用reset($save_name)取数组第一个元素作为文件名主体。 - 最后拼接路径:
$file_name = reset($save_name) . ‘.’ . $file_ext;
- 后端通过
- 攻击Payload构造: 我们构造一个特殊的数组作为
save_name:save_name[0] = “shell”(作为文件名主体)save_name[2] = “jpg”(作为文件后缀,跳过save_name[1])- 注意:
save_name[1]不存在或为空。 这样,is_array($save_name)为真,跳过常规检查。end($save_name)得到“jpg”,在白名单内,通过校验。reset($save_name)得到“shell”。最终拼接的文件名是“shell” . “.” . “jpg” = “shell.jpg”。 但是,这里存在一个致命的逻辑错误!在最终保存时,代码可能直接使用了$save_name数组的某个元素或拼接逻辑有误,导致实际保存的文件名与我们预期不符。更常见的利用方式是,通过控制数组索引,使得实际保存的文件名后缀是.php,而用于校验的后缀是.jpg。
- Burp Suite操作:
- 正常上传一个文件,抓包。
- 将POST body中的
save_name参数从字符串改为数组格式。原始可能是save_name=shell.jpg,修改为:
(注意:save_name[0]=shell.php& save_name[2]=jpgsave_name[1]被故意留空或省略,这会导致count($save_name)计算可能出错,进而影响后续拼接)。 - 发送请求。由于数组校验的逻辑缺陷,
.php文件被以.jpg的名义放行并保存。
4. 实战心得、防御建议与拓展思考
通关21关Upload-labs,不仅仅是一次技术练习,更是一次安全思维的洗礼。下面分享一些我个人的实战心得和对防御的思考。
4.1 常见问题与排查技巧实录
在复现过程中,你可能会遇到以下问题:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 上传成功但访问404 | 文件被上传到了非Web目录;文件名被修改;靶场路径配置错误。 | 查看源码中文件保存的路径$img_path;检查上传后的文件列表页面(如果靶场提供);使用Burp Suite查看响应包,里面可能包含文件路径。 |
| %00截断不生效 | PHP版本过高(>=5.3.4);magic_quotes_gpc配置为on。 | 切换PHPStudy到PHP 5.2.x 版本;检查php.ini中magic_quotes_gpc = Off。 |
| 图片马上传后无法解析 | 没有文件包含漏洞点;图片马制作不当,代码被破坏。 | 确认靶场环境是否存在文件包含页面(如include.php);使用WinHex对比上传前后图片的二进制差异,确保代码被插入在“安全区”。 |
| 条件竞争攻击始终不成功 | 时间窗口太短;并发量不够;脚本逻辑有误。 | 增加Intruder的线程数(如100+);优化Payload,让Webshell执行的动作更快速(如直接输出一个标志);尝试使用Python多线程脚本进行更精准的“撞车”。 |
| 蚁剑连接Webshell失败 | Webshell代码错误;路径错误;连接密码不对;服务器有额外防御(如disable_functions)。 | 使用<?php phpinfo();?>测试文件是否能被解析;检查蚁剑中填写的URL和密码是否与Webshell文件中的完全一致;查看phpinfo()输出,检查关键函数是否被禁用。 |
独家避坑技巧:
- 善用“查看源码”:Upload-labs每一关右上角都有“查看源码”按钮。在尝试攻击前,先花5分钟读懂源码,理解它的校验逻辑在哪里、怎么实现的,这比盲目尝试各种Payload有效率得多。
- Burp Suite的“Logger”和“Repeater”:使用Logger记录所有流量,方便回溯。对于复杂的修改(如%00、特殊字符),在Repeater中先用一个简单请求测试,确认修改效果后再正式攻击。
- 制作“通用测试脚-本”:不要总用
phpinfo()。准备一个更强大的测试Webshell,内容如:<?php echo “<pre>”; system(“whoami; pwd; ls -la”); echo “</pre>”;?>。这样可以一次性获取当前用户、工作目录和文件列表,信息量更大。 - 注意Windows/Linux环境差异:很多绕过技巧(如
::$DATA、后缀点空格)依赖于Windows文件系统的特性。如果靶场部署在Linux服务器上,这些方法可能无效。要关注靶场描述或源码中的路径分隔符(/还是\)来判断系统。
4.2 从攻击者视角看文件上传漏洞防御
通过攻击,我们更能理解如何防御。一个健壮的文件上传功能应该遵循“纵深防御”原则:
- 前端校验:仅为提升用户体验,可做但不依赖。
- 后缀名校验:使用白名单,而非黑名单。只允许
[‘jpg’, ‘jpeg’, ‘png’, ‘gif’]等必要的图片格式。并且校验应在服务器端进行。 - MIME类型校验:检查
$_FILES[‘file’][‘type’],但同样不可信,需结合其他手段。 - 文件内容校验:这是最关键的一环。使用
getimagesize()、exif_imagetype()等函数验证文件确实是有效的图片。甚至可以尝试用GD库等重新渲染图片,丢弃所有非图片数据(即二次渲染)。 - 重命名文件:上传后,使用随机算法(如
md5(uniqid().mt_rand()))生成新的文件名,并保留原始扩展名(从白名单中取)。这样可以防止攻击者预测文件路径,也能避免覆盖攻击、%00截断等。 - 控制文件权限:上传目录设置为不可执行。通过Web服务器(如Apache)配置,禁止该目录下任何文件的解析:
<Directory /path/to/upload> php_flag engine off </Directory>或RemoveHandler .php .php5 .phtml。 - 隔离存储:将上传的文件存储在非Web根目录下,通过一个专门的脚本(如
download.php?id=xxx)来读取和返回文件内容。这样即使上传了恶意脚本,也无法直接通过URL访问执行。 - 文件扫描:对上传的文件进行病毒/恶意代码扫描。
- 限制文件大小和频率:防止DoS攻击。
我个人在实际操作中的体会是,文件上传漏洞的攻防是一场“道高一尺,魔高一丈”的持续对抗。作为开发者,绝不能有“用了白名单就安全”的想法。必须将上述多种防御措施组合使用,特别是“内容校验+重命名+隔离存储”这三板斧,能抵御绝大多数自动化攻击和常见的手工测试。而作为安全研究者,则需要不断学习新的绕过技巧、了解不同中间件和语言的解析特性,并从代码审计的层面去发现那些深藏的逻辑漏洞。Upload-labs是一个完美的起点,但它不是终点。真实世界的应用,其代码逻辑往往比靶场复杂百倍,等待着你去探索和挑战。