abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · msquic-compat-vn-key-poison
MsQuic v2.6.1 (a01333cf) - Microsoft
兼容版本协商本应等到客户端能够真正处理新版本的报文之后再进行。此代码树在长包头看起来兼容的那一刻就提交版本号并重新生成 Initial 密钥,然后继续执行。解密失败的“撤销”会恢复版本号,但密钥仍停留在 v2。Handshake 类型或截断的报文甚至根本不会走到那个撤销逻辑。
来自服务器四元组的一个 UDP 数据报即可阻止默认客户端握手。进程保持存活。无 RCE。
| ID | 暂无 CVE |
| CWE | CWE-345, CWE-347, CWE-670 |
| CVSS | High: 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
| 产品 | MsQuic |
| 受影响版本 | v2.6.1 (a01333cf);我检查时 main 分支上仍然存在 |
| 认证 | 未认证 UDP,源地址必须是服务器四元组(路径上,或伪造服务器 IP 加客户端端口) |
| 许可证 | GNU Affero GPL v3.0 |
| 实验环境 | 仅 127.0.0.1 |
在 QUIC v1 握手期间,发送一个长包头数据报,其源地址和端口为服务器地址和端口。版本为 QUIC v2(RFC 9369,线上格式 6b 33 43 cf)。DestCidLength 为 0。默认客户端使用独占连接的 UDP 套接字,因此它们接受零长度 DCID。该库在 AEAD 之前就将其视为兼容版本协商,派生新的 Initial 密钥,然后要么永远不会触发解密失败的还原(Handshake 类型被延迟处理;截断的 Initial 会先返回 FALSE),要么还原 Stats.QuicVersion 而密钥仍停留在 v2。
真正的服务器继续使用 v1 通信。客户端使用 v2 盐值重传。该尝试永远不会到达 Connected。同一进程上的其他连接不受影响。这是握手 DoS,不是 RCE。
路径外攻击需要伪造服务器 IP 加上客户端的临时 UDP 端口。路径上攻击就足够了。独占绑定意味着攻击者不需要 CID。
我先阅读了 2026 年针对 microsoft/msquic 的 GHSA 浪潮:主机名 MITM(GHSA-w5f4-fx9m-m4q7,此标签即为该 cherry-pick)、路径 UAF RCE(GHSA-92f5-vc22-8j33)、ACK 下溢(GHSA-gvvw-8j96-8g5r)。这些漏洞点在 v2.6.1 上已关闭。
然后是报文头、CID、版本协商、重试、无状态重置、CIBIR。RFC 9368 兼容 VN 是遗留问题。QuicConnRecvHeader 看到版本不匹配,询问是否兼容,写入 OriginalQuicVersion,设置 CompatibleVerNegotiationAttempted,赋值 Stats.QuicVersion,调用 QuicCryptoOnVersionChange,然后继续执行。注释说传输参数稍后会检查 ChosenVersion。如果握手永远无法解密,那些 TP 永远不会到达。
解密失败看起来像是修复,直到你读到它:
Connection->Stats.QuicVersion = Connection->OriginalQuicVersion;
Connection->State.CompatibleVerNegotiationAttempted = FALSE;
没有第二次 QuicCryptoOnVersionChange。HKDF 标签仍停留在 v2。Handshake 类型报文被延迟处理(HANDSHAKE > ReadKey=INITIAL),且 RecvHeader 返回 FALSE,因此那个撤销永远不会执行。
我不需要 src/tools/attack。七个字节就够了。
已记录的弯路:在服务器的监听端口注入(客户端是独占连接的,它不会看到);在环回上先于毒化转发 Initial(握手先完成,毒化到达太晚);用 getchar() 作为示例服务器的等待(Docker 不是键盘);浅克隆导致 openssl 子模块为空(镜像必须获取 v2.6.1 并初始化 openssl);用任意垃圾长包头作为证明(不兼容的 11223344 仍然 Connected——有趣的部分是兼容的 v2)。花架子:CRYPTO 载荷、反向 shell、RCE。见证是握手结果。
cd lab
./run.sh
UDP 代理 127.0.0.1:18150 是客户端连接的目标地址。真正的 quicsample 服务器是 127.0.0.1:18151。在第一个客户端数据报上,PoC 从代理套接字发送截断的 v2 Handshake f06b3343cf0000,然后转发。将其绑定到环回。
control_connected=1
inject_connected=0
negative_connected=1
inject_hex=f06b3343cf0000
SUCCESS MSQUIC-COMPAT-VN-KEY-POISON-WITNESS
CONTROL 仅转发,必须 Connected。NEGATIVE 先注入 f0112233440000,必须仍然 Connected。INJECT 是唯一会失败的阶段。
在触发报文解密之前不要调用 QuicCryptoOnVersionChange。在提交后的每次失败时(RecvHeader 返回 FALSE 以及解密失败),恢复版本号并将密钥派生回 OriginalQuicVersion。忽略 Handshake / 0-RTT 作为兼容 VN 触发器。只有新版本的可解密 Initial 才应触发切换。