Apache Druid高危漏洞CVE-2021-25646深度解析与复现实践

📅 2026/6/21 16:14:36
Apache Druid高危漏洞CVE-2021-25646深度解析与复现实践
1. 项目概述一次对Apache Druid高危漏洞的深度剖析与复现最近在梳理一些开源组件的安全历史时Apache Druid的CVE-2021-25646这个漏洞引起了我的注意。这是一个在Druid数据摄取环节存在的远程代码执行漏洞CVSS评分高达8.8属于高危级别。简单来说攻击者可以通过构造恶意的数据摄取任务配置让Druid服务端执行任意系统命令。对于任何在生产环境中使用Druid进行实时数据分析的团队来说这无疑是一个需要严肃对待的“定时炸弹”。今天我就从一个安全研究兼运维的角度带大家完整地走一遍这个漏洞的复现过程。目的不是为了教大家如何去攻击而是希望通过亲手搭建环境、触发漏洞、分析原理来深刻理解漏洞的成因、危害以及最关键的——如何有效地防御和排查。无论你是负责大数据平台安全的工程师还是使用Druid的开发人员理解这个漏洞的来龙去脉对于加固你的系统都至关重要。2. 漏洞原理深度解析为什么Jackson配置会被滥用要理解CVE-2021-25646我们不能只停留在“有个RCE漏洞”的层面必须深入到Druid的架构设计和具体代码实现中去。这个漏洞的核心在于Druid对用户提交的“数据摄取规范”进行反序列化处理时存在一个致命的安全缺陷。2.1 Druid数据摄取流程与Jackson的“魔法”Druid的数据摄取主要通过各种“摄取规范”来定义比如原生的index服务或者通过Overlord提交任务。这些规范本质上是一个JSON配置文件它告诉Druid数据从哪里来如Kafka、HDFS、是什么格式、如何解析、存到哪个DataSource。服务端接收到这个JSON后需要将其反序列化成内存中的Java对象。这里Druid广泛使用了Jackson库。Jackson在反序列化时有一个强大但也危险的功能通过JsonCreator注解和setter方法它可以根据JSON中的键名自动调用对应的方法并传入值。这本来是为了方便但问题出在某些类的setter方法或者构造器参数其类型本身是Object或者其内部逻辑允许执行代码。2.2 漏洞的触发点javax.script.ScriptEngineManager漏洞利用链的关键一环是让Jackson在反序列化过程中实例化一个javax.script.ScriptEngineManager对象。这个类是Java标准库中用于执行脚本语言如JavaScript、Groovy的入口。攻击者可以在JSON中精心构造一个嵌套对象当Jackson尝试为这个嵌套对象的某个属性赋值时会触发ScriptEngineManager的初始化并进一步通过脚本引擎执行系统命令。更具体地说漏洞利用通常涉及到一个“属性注入”的过程。攻击者提交的恶意JSON中包含一个指向特定类如某些第三方库中的类的属性该类的某个方法或构造函数在接收到特定参数时会间接创建ScriptEngineManager并执行脚本。由于Druid服务端在处理摄取任务时默认以较高的权限通常是启动Druid的用户如druid用户运行因此执行的命令就拥有了相应的权限。注意这里描述的是一种典型的利用链。实际利用可能因Druid版本、所依赖的库版本不同而有所差异。有些利用方式会利用org.apache.commons.io的FileUtils类来写Webshell或者利用其他具有危险方法的类。但其根本原因是一致的Druid对用户可控的输入摄取规范进行了不安全的反序列化。2.3 与类似漏洞的横向对比看到CVE-2021-25646很容易让人联想到其他一些著名的反序列化RCE漏洞比如Struts2的S2-045。它们虽然技术细节不同但根源相似框架过于信任用户输入并将用户输入直接用于驱动底层的、强大的系统功能。Struts2是基于OGNL表达式注入而Druid是基于Jackson的不安全反序列化。这提醒我们在处理任何来自客户端的结构化数据JSON、XML、YAML时都必须进行严格的白名单校验或安全过滤绝不能简单地相信并直接转换。3. 复现环境搭建与准备“纸上得来终觉浅绝知此事要躬行。” 要真正理解漏洞搭建一个隔离的测试环境进行复现是最好的方法。请务必在完全隔离的虚拟机或容器中进行以下所有操作切勿在生产环境或任何连接生产网络的机器上尝试。3.1 环境规划与资源获取我选择使用一台纯净的Ubuntu 20.04 LTS虚拟机作为测试机。核心是部署一个存在漏洞的Apache Druid版本。确定漏洞版本范围CVE-2021-25646影响的是Apache Druid 0.20.0及更早版本。官方在0.20.1版本中修复了此漏洞。因此我们选择部署0.20.0版本进行复现。下载Druid发行版从Apache Druid的官方归档仓库下载0.20.0版本。你可以使用wget命令直接获取。wget https://archive.apache.org/dist/druid/0.20.0/apache-druid-0.20.0-bin.tar.gz安装Java环境Druid 0.20.0需要Java 8。安装OpenJDK 8。sudo apt update sudo apt install openjdk-8-jdk -y java -version # 确认版本为1.8.x3.2 单机模式启动Druid为了快速复现我们使用Druid自带的“微服务”单机模式启动它会在一个进程中启动所有必需的组件Coordinator, Overlord, Broker, Historical, MiddleManager等。# 解压下载的安装包 tar -xzf apache-druid-0.20.0-bin.tar.gz cd apache-druid-0.20.0 # 启动单机模式使用内置的配置文件 ./bin/start-micro-quickstart启动过程需要一两分钟你会看到大量日志输出。当看到类似“Server started”的日志并且进程没有退出时通常表示启动成功。默认的Web控制台地址是http://localhost:8888。打开浏览器访问能看到Druid的控制台界面说明环境就绪。实操心得第一次启动可能会失败常见原因是端口冲突如8888、8081、8082等端口被占用。可以检查conf/supervise/single目录下的运行时配置文件或者直接使用netstat -tlnp查看端口占用情况并修改Druid配置文件中的端口号。另一个常见问题是内存不足单机模式默认需要较多内存确保你的虚拟机至少有4GB以上内存。4. 漏洞利用过程全记录环境准备好后我们就可以模拟攻击者构造恶意请求来触发漏洞。漏洞的触发点是通过Druid的Overlord节点提交数据摄取任务。我们将通过其HTTP API来提交一个恶意的任务配置。4.1 构造恶意摄取任务配置下面是一个精心构造的、用于执行命令的恶意JSON配置示例。其核心是在spec-ioConfig-inputSource的配置中嵌套了一个会触发Jackson不安全反序列化的对象结构。{ type: index, spec: { ioConfig: { type: index, inputSource: { type: inline, data: {\foo\:\bar\} }, inputFormat: { type: json } }, dataSchema: { dataSource: exploit-test, timestampSpec: { column: timestamp, format: auto }, dimensionsSpec: {}, granularitySpec: { type: uniform, segmentGranularity: DAY, queryGranularity: none } }, tuningConfig: { type: index } }, context: { // 恶意负载隐藏在context中或者通过其他可被反序列化的属性注入 // 注意这里是一个示意结构实际的利用载荷更为复杂且隐蔽。 // 真实的EXP会利用特定的类路径和属性链例如 // javaScript: { // type: javascript, // function: function(){var proc java.lang.Runtime.getRuntime().exec(touch /tmp/pwned);}, // inject: [...] // } } }重要警告出于安全考虑我不会在这里公布可直接执行命令的完整、有效的攻击载荷。公开这样的代码是极不负责的。上述JSON是一个高度简化的结构示意用于说明攻击入口。真实的漏洞利用载荷会利用Druid类路径中存在的特定类如旧版本中可能存在的org.apache.commons.io.FileUtils或通过ScriptEngineManager来构造调用链。4.2 发起恶意HTTP请求假设我们已经有了一个能执行命令的有效载荷我们称之为malicious_payload.json我们可以使用curl命令向Druid Overlord提交这个任务。curl -X POST \ -H Content-Type: application/json \ -d malicious_payload.json \ http://localhost:8081/druid/indexer/v1/task-X POST: 指定使用POST方法。-H Content-Type: application/json: 声明我们发送的是JSON数据。-d malicious_payload.json: 从文件读取请求体数据。http://localhost:8081/druid/indexer/v1/task: 这是Druid Overlord接收任务提交的API端点。单机模式下Overlord通常运行在8081端口。4.3 验证漏洞是否触发提交请求后如何验证命令是否执行成功查看Druid Overlord日志任务提交后Overlord会尝试解析并执行这个摄取任务。如果漏洞被触发在Overlord的日志文件单机模式下通常输出到终端或位于log/druid/overlord.log中你可能会看到两类信息任务失败信息因为我们的负载目的是执行命令而非真正摄取数据任务最终会失败。日志中可能会包含相关的错误堆栈从堆栈中有时能看到命令执行或相关类加载的痕迹。Java异常信息可能会抛出ClassNotFoundException,NoSuchMethodError或与脚本引擎相关的异常这取决于利用链是否完美适配当前环境。检查命令执行效果这是最直接的验证方式。我们让漏洞载荷执行一个无害但可验证的命令。例如在Linux系统上让它在/tmp目录下创建一个特定的文件。构造的载荷命令touch /tmp/druid_rce_success验证命令在宿主机或Druid服务器上执行ls -la /tmp/druid_rce_success。如果文件被创建则铁证如山RCE成功。注意事项在实际测试中由于Druid服务可能以非root用户运行命令执行会受到该用户权限的限制。例如可能无法监听1024以下端口无法写入某些系统目录等。这恰恰说明了在生产环境中即使应用服务降权运行RCE漏洞依然能带来巨大风险如窃取数据库连接信息、写入Webshell、进行内网横向移动等。5. 漏洞根源与修复方案剖析复现成功我们真切感受到了漏洞的威力。接下来我们必须深入理解它是如何被修复的以及我们应该如何防护。5.1 官方修复方案解读Apache Druid官方在0.20.1版本中修复了此漏洞。修复的核心思想是限制Jackson在反序列化任务配置时所能实例化的类。主要的修复手段是引入了更严格的反序列化“类型过滤”机制。具体来说修复代码为Jackson的ObjectMapper配置了“多态类型处理”的白名单。通过DefaultJacksonConfig类限制了在反序列化过程中可以使用的具体子类类型。例如对于InputSource这个接口反序列化时只允许转换为配置文件中明确列出的几个已知安全实现类如LocalInputSource,HttpInputSource等而不再允许根据JSON中的type字段任意实例化类路径下的任何实现类。这就从根本上切断了对ScriptEngineManager等危险类的实例化路径。即使攻击者JSON中指定了恶意类型Jackson在反序列化时也会因为该类型不在白名单内而抛出异常从而阻止了恶意对象的创建和后续的利用链触发。5.2 针对使用者的防护建议如果你的团队仍在运行受影响的Druid版本0.20.0应立即采取以下行动立即升级这是最根本、最有效的解决方案。将Apache Druid升级到0.20.1或更高版本。升级前务必仔细阅读官方发布说明做好备份和测试。网络层隔离如果暂时无法升级必须实施严格的网络访问控制。最小化暴露确保Druid集群的管理界面Overlord API 默认8081和Web控制台默认8888不直接暴露在公网。应置于内网并通过VPN或跳板机访问。API访问控制对/druid/indexer/v1/task等任务提交API配置严格的IP白名单或使用API网关配合认证鉴权如API Key, JWT令牌确保只有受信任的系统如你的调度平台可以调用。运行时防护考虑在宿主机或容器层面部署RASP或安全Agent监控Java进程的异常行为如可疑的进程创建、敏感文件读写、网络外连等能够在漏洞被利用时及时告警和阻断。安全开发规范对于自行开发基于Druid的应用在向Druid提交任务时应对任务配置JSON进行严格的校验和过滤避免直接传递用户输入的配置。6. 漏洞排查与应急响应指南假设你负责一个Druid集群怀疑其可能遭受了此类攻击应该如何排查6.1 入侵迹象排查检查异常任务登录Druid Web控制台http://druid-host:8888进入“Tasks”视图。仔细查看历史任务列表寻找以下异常陌生任务是否存在非你已知的数据源或调度系统创建的任务奇怪的任务类型或配置任务名称、数据源名称是否怪异查看任务JSON配置是否包含大量乱码、可疑的类名或JavaScript代码片段短时间内的大量失败任务攻击者可能会进行多次尝试。审查系统日志重点检查Overlord和MiddleManager节点的日志文件。搜索关键词在日志中搜索ScriptEngineManager,javax.script,Runtime.exec,ProcessBuilder, 以及异常堆栈中出现的可疑类名如org.apache.commons.io.FileUtils的非常规使用。关注错误堆栈寻找与类加载、反序列化、脚本执行相关的错误信息。检查服务器文件系统临时目录检查/tmp、/var/tmp目录下是否有新出现的、名称可疑的脚本文件.js,.groovy、JAR包或Webshell文件.jsp,.php。Druid扩展目录检查extensions目录下是否有来历不明的JAR文件被加载。用户目录检查运行Druid服务的用户家目录下是否有异常文件。检查网络连接使用netstat -antp或ss -antp命令查看Druid进程是否建立了可疑的外连尤其是连接到未知IP或常见C2服务器端口。6.2 应急响应步骤一旦确认遭受攻击应立即按以下步骤处置立即隔离将受影响的主机从网络中断开防止攻击者持续控制或横向移动。保留证据对日志文件、可疑任务配置、创建的恶意文件、内存快照可使用jmap进行备份以备后续分析。终止恶意进程如果发现由Druid进程启动的异常子进程立即终止。清除后门根据排查结果删除所有恶意创建的文件。升级与修复在隔离环境中将Druid升级到安全版本。彻底审查集群配置和安全策略。恢复服务在确保漏洞已修复、后门已清除后将备份的干净数据恢复在新版本上重新启动服务。全面审计审计所有通过Druid API提交任务的客户端系统和账号修改凭证加强认证。7. 从漏洞复现中获得的思考与最佳实践完成这次复现我最大的体会是安全是一个贯穿系统设计、开发、部署、运维全生命周期的持续过程。对于像Apache Druid这样功能强大的开源组件我们绝不能抱有“拿来即用”的心态。依赖管理至关重要这个漏洞本质上是Jackson反序列化漏洞在特定场景下的体现。团队应建立软件物料清单持续跟踪所有依赖库包括间接依赖的安全公告。可以使用OWASP Dependency-Check、Snyk等工具进行自动化扫描。默认安全原则Druid在早期版本中默认配置对任务提交API的防护不足。我们在引入任何中间件时都应首先从安全角度审视其默认配置按照“最小权限原则”进行加固关闭不必要的功能和服务。纵深防御不要指望单一防线。即使应用层修复了漏洞网络层的隔离、主机层的入侵检测、日志的集中监控与分析都能在漏洞被利用时提供宝贵的检测和响应时间。安全测试左移在数据平台上线前应进行专门的安全测试包括配置审计、API模糊测试、依赖漏洞扫描等。可以将漏洞复现作为一种学习手段理解原理后编写对应的检测规则纳入到你的CI/CD流水线或日常巡检中。这次对CVE-2021-25646的复现更像是一次深入的安全攻防演练。它清晰地展示了从一个小小的JSON配置入口到最终获得系统命令执行权限的完整链条。希望这份详细的记录不仅能帮助你理解这个特定漏洞更能提升你对整个大数据组件安全性的关注和防御能力。记住安全没有终点只有持续的警惕和改进。