此仓库并非通用型漏洞利用 PoC 仓库。它是一个本地验证实验环境,用于复现并审查为 projectdiscovery/nuclei-templates 贡献的 CVE-2026-45185 模板的行为。
The nuclei code template is not a version-only detector.
It directly controls STARTTLS, BDAT, TLS close_notify, and a following
SMTP plaintext byte on the same TCP connection.
This sequence matches in the local vulnerable Exim 4.99.2 GnuTLS lab and does
not match in the patched Exim 4.99.3 GnuTLS lab under the same conditions.
当前的验证信号是远程 SMTP 响应 oracle,而非对 UAF 写入本身的直接观察。在内部,该 CVE 是一个释放后使用(use-after-free)问题:TLS 关闭后,换行字节(\r/\n)可能被写入已释放的 GnuTLS 传输缓冲区。实际上,客户端无法通过 SMTP 响应直接观察到该已释放缓冲区写入。因此,本 README 与模板并不声称直接证明 bdat_ungetc -> tls_ungetc 的 UAF 写入或 RCE。相反,该模板检测的是触发流程中出现的接收栈/状态恢复差异。
在追踪漏洞机制时,我观察到在 STARTTLS + BDAT 处理期间 TLS close_notify 之后,陈旧的 tls_* 函数指针可能残留在 BDAT 接收栈的下层,而未被正确恢复为 smtp_* 函数指针。这种状态会在服务器处理下一条 SMTP 命令时显现出来。当拆分后的 BDAT 首次完成后,在同一会话中发送 NOOP 会使存在漏洞的 Exim 4.99.2 GnuTLS 实验环境返回 421 lost input connection,而已修补的 Exim 4.99.3 GnuTLS 实验环境则以 250 OK 正常处理。这一清晰的漏洞/已修补响应差异被用作本地、授权 nuclei 代码模板的匹配器。
如需更深入的漏洞流程讲解,请参阅 CVE-2026-45185-Technical-Analysis.md。
通用 SMTP 信封收件人(RCPT TO):
Docker 实验环境的 lab_rcpt ACL 接受该信封收件人。在普通 SMTP 目标上,如果 RCPT TO 被拒绝,该序列可能无法到达 BDAT 正文解析器,因此模板需要一个被接受的收件人。
该值与 BDAT 正文中的 To: 头不同。RCPT TO 收件人是必须通过服务器收件人检查的 SMTP 信封地址。BDAT 正文中的 To: 值仅是邮件头文本;它不需要真实存在,也不需要被服务器作为邮箱接受。
templates/CVE-2026-45185.yaml
模板名称:
Exim 4.97-4.99.2 GnuTLS STARTTLS BDAT - Same-Session Response Check
该模板以 metadata.verified: true 编写,并带有 code 和 intrusive 标签。
在 POC_2026_45185/ 目录下运行以下命令。
docker compose build
docker compose up -d
验证模板语法:
nuclei -duc -validate -code -t templates/CVE-2026-45185.yaml
运行前签署本地代码模板:
nuclei -duc -code -t templates/CVE-2026-45185.yaml -sign
针对存在漏洞的实验环境运行:
nuclei -code \
-t templates/CVE-2026-45185.yaml \
-u 127.0.0.1:2525 \
-var [email protected] \
-debug
预期结果:
CVE-2026-45185: vulnerable response oracle matched
针对已修补的实验环境运行:
nuclei -code \
-t templates/CVE-2026-45185.yaml \
-u 127.0.0.1:2526 \
-var [email protected] \
-debug
预期结果:
no match
NO-MATCH: patched-like response: NOOP succeeded after split trigger
请注意,nuclei 的 code 协议默认不会执行,因此必须使用 -code。Nuclei 还会阻止未签名的 code 模板。如果本地 nuclei 私钥受口令保护,请在交互式终端中运行签名命令并输入该口令。由于摘要覆盖模板内容,因此每次模板变更后都需要重新签名。
这些截图展示了模板签名后本地实验环境的响应 oracle 结果。它们是上述同会话响应差异的验证证据,而非内部 UAF 写入的直接调试器或 ASAN 证明。
127.0.0.1:2525 上存在漏洞的 Exim 4.99.2 GnuTLS 实验环境:

127.0.0.1:2526 上已修补的 Exim 4.99.3 GnuTLS 实验环境:

该 CVE 的核心不是 SMTP 横幅或版本检查。模板需要在同一 TCP 连接上创建以下传输状态转换:
plaintext SMTP EHLO
-> STARTTLS
-> TLS handshake on the same TCP connection
-> TLS EHLO / MAIL FROM / RCPT TO / BDAT 70 LAST
-> first 69 bytes of the BDAT body as TLS application data
-> TLS close_notify without closing the TCP socket
-> final body byte as plaintext on the same TCP connection
-> same-session plaintext NOOP response check
YAML 模板中的 Python 代码仅在以下所有条件通过后打印固定标记。nuclei 匹配器仅匹配该标记。
Exim identity is found
AND STARTTLS is advertised in plaintext EHLO
AND CHUNKING is advertised in plaintext EHLO
AND STARTTLS is accepted
AND CHUNKING is advertised in TLS EHLO
AND MAIL FROM is accepted
AND RCPT TO is accepted
AND the split close_notify BDAT reaches first completion
AND first completion contains "250 OK id="
AND the same-session plaintext NOOP response contains "421"
AND the same-session plaintext NOOP response contains "lost input connection"
该模板不会仅基于以下任一信号进行匹配:
version-only
timeout-only
connection-drop-only
empty-response-only
recipient rejection
STARTTLS/CHUNKING advertisement only
两个实验环境都会处理拆分后的 BDAT 消息直至首次完成。
250- 70 byte chunk, total 72
250 OK id=...
当在同一条 SMTP 会话上发送下一条 SMTP 明文命令时,差异就会显现。
存在漏洞的 4.99.2:
NOOP -> 421 exim-lab.local lost input connection
QUIT -> 421 exim-lab.local lost input connection
RSET -> 421 exim-lab.local lost input connection
已修补的 4.99.3:
NOOP -> 250 OK
QUIT -> 221 exim-lab.local closing connection
RSET -> 250 Reset OK
解释:
Observation:
Both labs reach split BDAT message completion.
Only the vulnerable lab fails to return cleanly to the next plaintext SMTP
command loop in the same session.
Evidence:
The vulnerable follow-up response is 421 lost input connection.
The patched follow-up response is 250 OK or 221 closing connection.
Inference:
This difference is consistent with the receive stack/state recovery difference
after STARTTLS close_notify.
模板使用以下 70 字节消息作为 BDAT 正文。
From: [email protected]\r\n
To: [email protected]\r\n
Subject: poc\r\n
\r\n
body
SMTP 信封收件人通过模板变量 recipient 单独传入。正文中的 To: 头只是文本,与 SMTP RCPT TO 是否被接受无关,因此它不需要真实存在,也不需要被服务器接受。
拆分形态:
BDAT 70 LAST
TLS body: first 69 bytes, ending with "bod"
TLS event: close_notify
Plaintext: final byte "y"
Follow-up: NOOP
您可以手动验证两个实验环境是否都通告了 Exim、STARTTLS 和 CHUNKING。
printf 'EHLO lab-client.local\r\nQUIT\r\n' | nc -w 3 127.0.0.1 2525
printf 'EHLO lab-client.local\r\nQUIT\r\n' | nc -w 3 127.0.0.1 2526
预期信号:
Exim
STARTTLS
CHUNKING
您还可以检查正常的 STARTTLS 路径:
openssl s_client -starttls smtp -connect 127.0.0.1:2525 -crlf
openssl s_client -starttls smtp -connect 127.0.0.1:2526 -crlf
debugging/ 和 notes/source-walkthrough-progress.md 下跟踪。POC_2026_45185/
compose.yaml
images/ local nuclei validation screenshots
vulnerable/ Exim 4.99.2 + GnuTLS debug build
patched/ Exim 4.99.3 + GnuTLS debug build
nuclei-templates/ submodule: local nuclei template workspace
| 目标 | 版本 | TLS 后端 | STARTTLS | CHUNKING | 端口 | 预期 nuclei 结果 |
|---|
| 存在漏洞 | Exim 4.99.2 | GnuTLS | 是 | 是 | 127.0.0.1:2525 | 匹配 |
| 已修补 | Exim 4.99.3 | GnuTLS | 是 | 是 | 127.0.0.1:2526 | 不匹配 |