漏洞
Libreswan CVE-2026-12413——IKEv2 分片重组中的满表断言
CVE-2026-12413 让未完成身份认证的 IKEv2 对端在受保护的分片消息中恰好占满 30 个载荷描述符,Libreswan 4.6 至 5.3 随后把合法计数误判为内部不变量破坏并主动终止 pluto ,远程攻击者可据此反复中断 VPN 控制面,5.3.1 已修正这项边界判断。

文章导航
1 第 30 格填满后,VPN 进程退出
六月末的一行补丁看起来小得容易被略过:< 后面多了一个等号。它没有调整数组长度,没有更换内存分配器,也没有重写 IKEv2 状态机。可这一个字符决定了一台 VPN 网关面对合法满容量状态时,是继续处理协议错误并保持服务,还是在网络输入到达后调用 abort()。CVE-2026-12413 的全部故事,都藏在“30 是合法数量,还是非法下标”这个问题里。
受影响的是 Libreswan 的 IKE daemon pluto。攻击者先完成 IKE_SA_INIT,获得本次 IKE SA 的加密与完整性密钥,再发送采用 RFC 7383 分片的 IKE_AUTH 消息。第一片可以携带一串外层载荷,末尾才是 Encrypted and Authenticated Fragment,也就是 SKF。只要外层解析结束时 msg_digest.digest_roof 恰好等于 30,旧版重组函数就会命中失败断言。
这一刻并没有第 31 次数组写入。下标 0 至 29 已经保存 30 份载荷描述符,digest_roof 指向末尾后一位置。重组函数从现有 SKF chain 取回已经分配的描述符,在原槽位上把它改写成 SK。旧断言却要求 roof 严格小于容量,相当于拒绝了“数组刚好装满”这个状态。Libreswan 的 passert 是致命断言,失败路径最终进入 C 库 abort()。
服务管理器可能在数秒内重新拉起 pluto,但恢复并不等于风险消失。进程中的 IKE SA 管理状态、正在认证的对端、重协商和 DPD 调度都会中断;内核里尚存的 XFRM state 可能让部分旧流量短暂继续,使监控产生一种“隧道还通”的错觉。攻击者在 UDP 500 或 UDP 4500 恢复监听后再次触发,就能把控制面维持在退出、启动、重新协商的循环里。
Libreswan 公告将 4.6 至 5.3 列为受影响版本,5.3.1 是上游首个固定版本。CNA 给出的 CVSS 3.1 向量为 AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H,基础分 7.5。这个评分准确捕捉了它的边界:网络可达、条件稳定、不要求有效账户,公开源码证据指向可用性破坏;重组路径没有越界写入,也没有形成远程代码执行原语。
代码与处置可以先收束在这里:固定树中,programs/pluto/demux.h 用 PAYLIMIT 和 msg_digest.digest[] 定义容量,错误比较位于 programs/pluto/ikev2_message.c 的 reassemble_v2_incoming_fragments()。4.6 至 5.3 需要升级到 5.3.1 或等效回移,并重启到新二进制。只有确认所有 IKEv2 connection 都不需要 RFC 7383 时,才可临时设置 fragmentation=no;ACL 与限速只能缩小触发机会,不能替代永久修复。
1.1 攻击发生在身份认证完成之前
“受保护的 IKE_AUTH”很容易被读成“经过身份认证的消息”,两者在 IKEv2 中是两个时间点。RFC 7296 的 IKE_SA_INIT 交换协商算法,交换 nonce 与 Diffie–Hellman 公钥,并由双方推导 SK_d、SK_ai、SK_ar、SK_ei 和 SK_er。只要攻击者按协议完成这一步,就掌握自己作为 initiator 应持有的加密与完整性密钥。
身份、证书、EAP 或预共享密钥证明进入后续 IKE_AUTH。于是,一个尚未被网关认可的人已经能够生成通过 ICV 校验的 SK 或 SKF。这个顺序是协议建立保密认证通道的基础,也让 IKE_AUTH 解密、分片收集和载荷解析天然位于身份信任边界之前。网关不能以“包有正确认证标签”为理由把发送者视为已授权用户。
触发者仍需维护一条结构正确的 IKE SA,使用协商出的密码套件,为每片设置匹配的 SPI、message ID、exchange type、fragment number、fragment total、IV、padding 与完整性标签。这些工作可以由修改后的 IKE 实现自动完成,复杂度不会构成高门槛。它不需要窃取现有用户密钥,也不需要让证书校验通过。
因此,暴露面应从“哪些连接接受 IKEv2 和 fragmentation”开始盘点,不能从“哪些用户拥有 VPN 凭据”开始。公网远程接入、站点到站点集中器、云边缘节点和合作伙伴入口只要允许匿名发起 IKE_SA_INIT,便会到达这条预认证解析链。源地址 ACL、上游 DDoS 过滤与速率限制可以降低触发频率,版本修复仍是关闭代码缺陷的唯一长期措施。
1.2 一次退出怎样扩散成业务中断
pluto 是协调 IKE 协商、Child SA 建立、重密钥、失活检测和内核策略安装的用户态核心。断言终止的是整个 daemon,不是单个恶意 exchange。已有 ESP 数据面在不同部署中可能继续一段时间,新连接、到期重密钥与策略更新则立即失去控制者。业务影响会沿各条 SA 的剩余寿命逐步展开,看起来像一场从边缘向内扩散的故障。
自动重启会创建第二层压力。大量 peer 同时重新认证,证书验证、DNS、LDAP、RADIUS、PKCS#11 和硬件安全模块负载随之上升;HA 节点接管时还要承受状态不同步与流量回灌。连续触发可能撞上 systemd 的启动频率限制,原本几秒的重启最终变成需要人工介入的 failed unit。
诊断时,端口探测与单条长期 ESP 流量都不够。应同时观察 service restart counter、pluto PID、IKE_SA_INIT/IKE_AUTH 成功率、Child SA 数量、重密钥延迟、认证后端请求量和 HA 角色切换。只有把进程、协议与业务三个时间轴叠在一起,才能看清一次 abort() 对网关群的实际影响。
这也是本报告把它按高危可用性漏洞处理的原因。它没有公开的持久化、提权或数据读取链路,却位于组织外部最重要的加密入口之一;无需有效身份的对端可以重复到达;服务管理器提供的自动恢复会被同一输入再次打断。修复窗口应由网关可达性和承载业务决定,不能只依据单次停顿时长。
2 第一片为什么能把载荷表推到边缘
理解触发条件,先要把两种“分片”分开。普通 IP fragmentation 由网络层拆包,任何一片丢失都会使整份 IP 数据报失效,中间设备也经常丢弃碎片。RFC 7383 把拆分移到 IKE 层:发送者先拥有 IKE SA 的密钥,再把一份加密消息分成多个独立受到认证的 SKF payload。每片都是完整 IKE 消息,可以单独校验并按序号收集。
这种设计主要服务于证书链庞大的 IKE_AUTH。IKE_SA_INIT 本身不能使用 RFC 7383,因为此时加密与完整性密钥尚未建立。双方在初始交换中用 IKEV2_FRAGMENTATION_SUPPORTED 通知能力,随后才可将较长的受保护请求或响应拆片。Libreswan 配置项 fragmentation=yes 默认启用这项能力。
SKF 是外层载荷链的最后一项。第一片的 SKF header 中,Next Payload 指向重组后明文的首个内部 payload;后续片的该字段必须为 NONE。RFC 7383 还保留一个少见但重要的布局:第一片可以在 SKF 前携带未加密的外层 payload,后续片不得重复这些外层元素。接收者因而需要保留第一片的完整 msg_digest,再把其他片的明文拼回它。
载荷链由每个通用头中的 Next Payload 字段串起来。解析器从 IKE header 给出的首个类型出发,读取当前头、长度与 critical bit,再移动到下一项,直到 NONE。攻击者无需让每个外层载荷承担复杂业务语义;只要它们符合通用头和长度规则,就能推动解析状态。主开发线的回归利用 unknown noncritical 类型,维护线则必须考虑其不同的计数实现。
2.1 RFC 7383 给第一片留下的特殊结构
一组合法片段至少包含片号与总片数。片号从 1 开始,总数对该组保持一致;第一片的 inner Next Payload 不得为 NONE,第二片及以后必须为 NONE。发送者用同一 exchange type、message ID 与 IKE SA,接收者按 initiator/responder role 将它们放入对应 fragment set。所有片都通过完整性验证后,才拼接加密容器中的明文。
Libreswan 将最大片数设为 32,并为索引 1 至 32 分配 MAX_IKE_FRAGMENTS + 1 个元素,下标 0 明确留空。ignore_v2_incoming_fragment() 会拒绝片号 0、片号大于总数、总数超过 32、首片缺少 inner next payload、后续片携带非零 inner next payload以及与既有集合不一致的 total。重复片也不会被当作新成员计数。
这些约束说明触发不依赖畸形片号或数组越界。攻击者可以使用两片或更多结构完整的 SKF,每片都位于允许范围,ICV 正确,总长度也在 UDP 输入上限内。缺陷只在全部片到齐、进入重组且第一片已经让全局 payload digest 表处于满容量时显现。
“外层未加密”不代表可以在网络上随意篡改。它位于整个 IKE 消息的认证范围,修改 header、payload bytes 或链条都会让完整性校验失败。攻击者是本次 IKE SA 的合法协议发起者,拥有自己方向的 SK_ai,因此能够选择并认证这条外层链。这再次把入口定位在预身份、已建密钥的阶段。
2.2 unknown payload 在两条源码线上并非同一种记账
上游主开发线曾在提交 6824359f416a1eba0fabc0f19d5fe9267338bf3b 中调整未知 IKEv2 payload 的保留方式。当前主线解析循环在容量检查后直接取得 &md->digest[md->digest_roof++],即使类型没有专用 descriptor,也会用通用头完成解析并占用一个 digest 槽位。于是 29 个 unknown noncritical 外层 payload 加最后一个 SKF,能够稳定得到 roof 30。
5.3.1 维护线的同名函数形状不同。它先令 pd = md->digest + md->digest_roof,未知类型分支读取通用头后直接 continue,循环底部的 digest_roof++ 没有执行。多个 unknown payload 会反复使用同一临时槽位,最终只有后面的已识别载荷推进计数。因此,主线 KVM 测试中的“29 unknown + SKF”不能原样解释维护版本的每一步。
官方公告用“payload 数量能够填满数组”描述漏洞,结论对两个分支都成立。维护线要抵达完整计数,需要用会实际记账的已识别外层 payload 组合,再以 SKF 收尾;具体组合还受到各类型出现次数、critical 规则和消息语义验证影响。研究报告必须保留这种版本差异,避免把测试 harness 的便利构造写成跨版本唯一网络载荷。
这项差异不改变受影响范围,也不削弱修复必要性。漏洞根因位于重组前置条件,它只关心到达时 roof 是否为 30。分支特有的 parser 行为决定怎样构造这个状态;一旦状态成立,旧版都执行同一错误比较。下游产品 fork 若改过 unknown handling,更应以自身源码与运行期断点确认 payload 组合。
从检测角度看,不能只写一条“29 个未知类型”的固定签名。主线回归流量会命中,维护分支或厂商 fork 可以用不同已识别载荷到达同一 state。更稳健的检测模型应统计第一片 SKF 前的实际 digest 消耗、验证链条闭合,并把满表状态与 daemon 退出关联;网络侧若无法还原实现计数,就把高密度外层载荷作为调查信号,交给主机证据完成确认。
3 30 格表如何表示一条 IKE 消息
网络报文进入 pluto 后,不会被直接映射成一组长期存在的业务对象。Libreswan 先建立 struct msg_digest,在其中保存 IKE header、原始 packet buffer、解析后的 payload 描述符、按类型链接的 chain、接收端点和状态机定位信息。它是一份贯穿 demux、认证、解密与状态转换的短期工作台。
programs/pluto/demux.h 定义 PAYLIMIT 30,随后在 msg_digest 内嵌 struct payload_digest digest[PAYLIMIT] 和 unsigned digest_roof。固定数组避免为每个小载荷反复分配内存,也给不可信报文设置明确的解析资源上限。每个 payload_digest 保存解码头的 union、当前载荷 body 的 pbs_in、类型、next 指针及所属 chain。
digest_roof 的名字来自半开区间:已使用元素构成 [0, roof)。空表时 roof 为 0;写入第一项使用下标 0,随后 roof 成为 1;写入第 30 项使用下标 29,随后 roof 成为 30。所有有效元素都满足 index 小于 roof,而 roof 自身可以等于数组元素数。它既是计数,也是末尾迭代器。
固定容量代码在“恰好填满”时最容易发生语义错位,明显超限通常已有直接防线。写入端需要在索引前检查 roof >= capacity;遍历端需要以 index < roof 结束;只检查状态是否仍处于数组表示范围时,应允许 roof <= capacity。这三个表达式看起来只差等号,分别回答“还能写吗”“还有元素吗”“当前计数合法吗”。
Libreswan 的解析器正确执行了写入端检查。每轮开头先判断 md->digest_roof >= elemsof(md->digest),容量已经耗尽便把 summary 设为 v2N_INVALID_SYNTAX 并停止。只有尚有空间时才取得描述符地址。CVE 的错误位于另一个阶段:重组函数没有准备写下一格,却套用了“还能写”的严格条件。
3.1 parser 同时维护数组顺序与类型 chain
ikev2_decode_payloads() 接收起始 Next Payload、输入流、需要记录的 payload set 和 logger。循环先核对全局 digest 容量,再根据类型选择 struct_desc,用通用或专用结构描述读取固定头,验证声明长度,建立 body 子流。解析成功后,已识别类型会挂入 md->chain[np],同类型的重复元素通过 next 串联。
数组保留报文中的总体解析顺序,chain 提供按类型快速访问。重组函数并不搜索“第几个元素是 SKF”,它直接读取 md->chain[ISAKMP_NEXT_v2SKF]。后续状态机也可通过 SK、CERT、AUTH、NOTIFY 等 chain 找到需要的 payload。一个描述符同时属于顺序表和某条类型链,改写时必须同步两个视图。
parser 的容量是整份 msg_digest 的预算。第一片外层载荷消耗一部分;SKF 重组成 SK 后,内部明文还要继续解码,复用同一张表中剩余槽位。如果外层已经用满 30 格,重组本身可以完成,但内部首个 payload 没有描述符空间。正确行为是由 ikev2_decode_payloads() 返回 INVALID_SYNTAX,让状态机拒绝这次 exchange,同时保持 daemon 运行。
上游满表回归的成功标准不是“隧道最终建立”,也不是“30 个外层元素都被业务接受”。它证明 roof 30 可以安全经过 SKF 到 SK 的原位重组;随后的内部 payload decode 正常发现预算耗尽,响应或记录协议语法错误。安全边界从致命进程断言退回可恢复的对端错误。
这种行为也解释了为什么补丁没有扩大 PAYLIMIT。把数组改成 31 只会把崩溃状态向后推一格,且改变全局解析预算。上游保留 30 个描述符的资源约束,只让重组接受合法满表;任何真正尝试解析第 31 份记录的路径仍由既有 >= 检查阻止。
审计同类代码时,应画出每个计数变量的写入点、索引点、迭代点和纯状态检查。单看变量名或单行 diff,很难判断等号是否安全。将数组与 chain 两个视图叠在一起,才能看到 reassemble_v2_incoming_fragments() 取得的是已存在的对象,后续 *skf = sk 也是原位覆盖。
3.2 SKF 被改写为 SK,没有第 31 次分配
全部片段验证完成后,重组函数首先对第一片保存的 msg_digest 增加引用。它断言 SK chain 为空、SKF chain 非空,并期望该 SKF 来自片号 1。这些条件描述当前对象的形状:还没有普通加密容器,第一片的 SKF descriptor 将成为重建容器的锚点。
第一遍循环计算所有 fragment plaintext 的总长度,第二遍分配一块连续 raw_packet 并依次复制明文。随后代码构造局部变量 struct payload_digest sk,让其 pbs 指向这块拼接缓冲区,设置 SK payload header 和 chain 信息。局部结构只是准备写入的值,不会进入 digest[] 的新位置。
真正的转换只有三步:从 SKF chain 保存 skf 指针,将 SKF chain 清空,把 SK chain 指向同一个 skf,最后执行 *skf = sk。源码注释使用 “scribble” 描述覆盖动作。地址没有改变,数组长度没有改变,digest_roof 也没有递增。
若 SKF 原来位于 digest[29],改写后的 SK 仍在 digest[29]。roof 为 30 继续准确表达已用区间 [0, 30)。因此修复表达式 md->digest_roof <= elemsof(md->digest) 对当前操作足够严格:它允许 0 至 30,拒绝任何已超出结构表示能力的值。
固定树中全部 digest_roof 使用点显示:IKEv1 与 IKEv2 parser 都在索引前进行容量检查;dump 与诊断逻辑用小于 roof 的循环;clone 和释放沿实际已用元素迭代;本次重组通过 chain 取得目标。修复后的等号不会放行下标 30 的访问,也没有另一处把 roof 当作最后有效下标的确认漏洞。
这类证据比“补丁由上游提供”更重要。一个 assertion 放宽可能掩盖真实越界,只有证明后续代码不追加、不索引 roof、不把满表状态传给错误长度的循环,才能确认修复没有用可用性换内存安全。对设备厂商的回移评审也应重复这项检查,因为 fork 中的重组实现可能已发生变化。
4 从 UDP 报文到 abort() 的完整函数链
漏洞分析常在断言那一行停住,运营团队却需要知道输入怎样穿过前置验证、哪一层拥有可观测日志、core stack 为什么会出现这些函数。固定版本中的处理链可以从收到已关联 IKE SA 的报文开始追踪:外层 demux 建立 msg_digest,IKEv2 decoder 填充 payload table,secured-message 分派识别 SKF,fragment collector 保存片段,验证与解密成功后调用 reassembly。
不同历史版本的上层函数名称略有变化。当前主线在 process_packet_with_secured_ike_sa() 附近处理受保护消息;早期 4.x 代码在 ikev2_process_state_packet() 中显式分支。核心顺序保持一致:解析外层链,找到 IKE SA,确认交换状态,收集 SKF,验证每片完整性,重组,再解码 SK 内部 payload。
collect_v2_incoming_fragment() 不是无条件缓存。它先调用 fragment 规则验证,按消息角色定位 st_v2_incoming,为首片保存 msg_digest 引用,并把加密文本、IV offset 与片号放入集合。总数未齐时状态机暂时返回;最后一片补齐后才进入 decrypt/reassemble 分支。
每片都经 verify_and_decrypt_v2_payload() 使用本方向 IKE 密钥验证。任何 ICV 错误都会被丢弃,无法触发重组断言。攻击者必须发送密码学上自洽的片组,这正是完成 IKE_SA_INIT 后具备的能力。网络中间人若不知道密钥,篡改现有用户片段不会得到相同效果。
旧版进入 reassemble_v2_incoming_fragments() 后依次检查 chain 形状,然后计算 30 < 30。passert 宏构造包含源码位置与表达式的 logjam,调用标记为 NEVER_RETURNS 的 passert_logjam_to_logger()。lib/libswan/passert.c 先把日志写到 logger,紧接着调用标准库 abort()。
4.1 致命断言为什么影响整个 daemon
passert 用于表达开发者认为运行期绝不应出现的内部不变量。与 pexpect 或返回 INVALID_SYNTAX 的协议校验不同,它没有为不可信输入设计恢复分支。失败后记录断言、文件和行号,再以 SIGABRT 结束进程。CVE 的严重性来自一个外部可构造状态被错误归入了内部不变量。
在 systemd 部署中,journal 往往先出现 assertion 文本,随后是 unit 的 code=dumped 或 status=6/ABRT,再跟随自动 restart。core backtrace 应包含 passert_logjam_to_logger、重组函数及其上层 secured-message 处理。发行版是否生成 core 取决于 LimitCORE、systemd-coredump、容器权限和磁盘策略,未发现 core 不能排除触发。
进程重启后,原先内存中的 fragment set 已消失,攻击者需要重新完成 IKE_SA_INIT 并发送片组。这只增加少量往返。若入口由负载均衡分发,攻击序列可能命中多台节点;若设备使用单进程管理全部租户,单个来源触发会跨越连接配置影响共享控制面。
数据面是否立刻断开由平台决定。Linux 内核中的 ESP SA 与 policy 不会因为用户态进程退出就同步清空所有条目,旧会话可能继续转发;当 lifetime、rekey、DPD 或路由变更到来时,它们逐渐失效。检测应将“已有 ping 仍通”视为有限证据,主动建立新隧道并等待至少一个 rekey 周期才能评估恢复完整性。
如果 supervisor 配置了 restart burst 限制,重复崩溃可能把 unit 留在 failed 状态。如果没有限制,持续拉起会消耗 CPU、熵、证书与认证后端资源。两种策略都无法替代修复:前者延长停机,后者让攻击者获得稳定的控制面抖动器。
容器化网关还可能由 Kubernetes 重建 Pod。readiness probe 很快恢复绿色,但连接状态已经重置;重建速度、镜像拉取与节点调度把单次 SIGABRT 扩大成更长的不可用窗口。应在集群层关联 Pod restartCount、IKE 错误率和外部 UDP 流量,避免把它误判成普通滚动发布。
4.2 相邻边界审计没有发现第二个确认漏洞
相邻边界审计覆盖 fragment header、收集数组、总长度计算、连续缓冲区分配和 digest 使用点。结果支持上游定性:当前确认影响是可达断言拒绝服务,没有证据表明同一路径还包含独立的越界读写。
片数组采用 MAX_IKE_FRAGMENTS + 1,注释明确说明协议编号从 1 开始并故意留空元素 0。输入验证先拒绝 number 0,再保证 number 不大于 total、total 不大于 32。后续循环均使用 1 到 total 的闭区间,最大索引 32 对应已分配的最后一个元素。这组边界彼此匹配。
单个 UDP 输入上限为 65,536 字节,最多 32 片的明文总量约为 2 MiB。重组变量 unsigned size 在常见 32 位 unsigned 上距离溢出仍很远;每次复制长度来自已验证片段缓冲区,目标空间按相同长度求和分配。我们没有在这组公开约束下建立整数回绕或目标 buffer under-allocation。
重复片不会让 count 超过 total。集合会检查已有槽位,片组的 total、exchange 与 message context 不一致时拒绝合并;完整性失败会减少或清理有效片数。攻击者仍可用大量半完成 IKE SA 或片组消耗资源,那属于协议服务常见的容量防护问题,不能在没有独立证据时包装成这次 CVE 的新漏洞。
全部 digest_roof 用法也经过检索。索引点之前存在容量条件,遍历使用半开区间,修复后的重组只判断当前状态。主线保存 unknown payload 的改动扩大了可触达构造,却仍在递增前检查空间。没有发现 roof 大于 30 可从网络输入自然形成。
这项负面结论同样属于研究成果。安全报告需要区分“审计过并未建立利用条件”与“从未查看”。我们保留了检查范围、固定提交和假设,后续若厂商 fork 放宽最大片数、改变 input size、追加新 descriptor 或改写 unknown 分支,可以据此重新计算,而无需从头猜测。
5 一字符补丁为何能恢复正确语义
维护分支提交 5c4369afcfe95a924ee0a96a0b29a7204dc40504 与主线提交 6088d294b8a6448df1611b98d778d85482646586 做了同一件事:把重组入口的 digest_roof < elemsof(digest) 改成 digest_roof <= elemsof(digest)。补丁说明直接指出 DIGEST_ROOF points at the roof,用变量的抽象语义解释等号。
这项改动没有绕过 parser 的上限。真正准备取得 digest[roof] 的代码仍要求 roof 小于容量;重组只确认当前计数处于可表示区间,然后通过 SKF chain 获取元素。roof 等于 30 时,后续连续 buffer 由 fragment plaintext 总长度单独分配,payload descriptor 则在原位置覆盖。修复精确对应操作所需的前置条件。
若只看断言文本,会担心等号让越界状态继续执行。沿数据依赖检查可以消除这项疑虑:没有表达式使用 digest[md->digest_roof],没有递增 roof,局部 sk 被赋给现有 skf 指针。下一次真正索引数组发生在解码内部明文时,开头的 >= 检查立即发现表已满并返回语法错误。
安全补丁的规模与验证规模没有线性关系。一字符 diff 必须回答至少四个问题:变量究竟是 index 还是 count;等于容量的对象状态是否可能从有效输入形成;断言之后有没有索引或追加;边界状态接下来以何种协议结果收束。上游提交给出了第一和第三个答案,历史与回归补足了第二和第四个答案。
对维护版本回移时,不宜用“删除 passert”代替。完全移除不变量会让真正的 roof 大于 30 静默前进,也降低 future regression 的可见性。应保留 <= 断言、parser 的 >= 容量拒绝、SKF/SK chain 形状断言与 KVM 边界用例,让三道约束各自承担明确职责。
5.1 修复后的正确终点是可恢复语法错误
主线测试 cve-2026-12413-max-payloads 通过 impairment 在 IKE_AUTH 外层加入 29 个 unknown v2 payload,并启用 fragmented exchange。首片 parser 把 29 个 unknown 与一个 SKF 记成 30 个 digest。旧版在所有片到齐后退出;修复版允许重组继续。
重组把 SKF 改为 SK,却没有为 SK 内部的第一个 payload 腾出槽位。ikev2_decode_payloads() 看见 roof 已经等于数组容量,记录 “more than 30 payloads in message; ignored”,把 summary notification 设为 INVALID_SYNTAX。测试的预期 console 最终看到对端拒绝,隧道保持未建立。
这个结果非常有价值。它证明补丁没有因为接受 roof 30 而跳过全局资源限制,也没有把异常消息当作成功认证。对端输入仍被拒绝,只是拒绝发生在为外部数据设计的错误通道中。daemon 继续服务其他连接,systemd restart counter 不增加,后续正常 peer 能照常建立隧道。
回归验收应把“进程存活”与“恶意隧道失败”同时写入断言。只看 exit code 可能遗漏 daemon 在后台重启;只看 tunnel down 可能把旧版崩溃当成测试通过。应记录测试前后 PID、service invocation ID、core 数量、journal assertion、恶意连接状态以及一条正常控制连接的 IKE/Child SA。
相邻边界至少覆盖 28 个填充载荷加 SKF、29 个填充载荷加 SKF、真正尝试超过 30 份记录,以及非分片 SK。具体填充类型按目标分支的 parser 语义选择。每组还应变换片数、到达顺序、重复片和丢片,确认修复没有破坏 fragment collector 的既有拒绝逻辑。
下游设备常常隐藏上游 KVM 框架。可以在隔离网络中使用已修改的 Libreswan peer 或协议测试器生成等价报文,并在目标端打开足够的 debug 日志。测试必须使用授权设备与测试凭据,限制速率和范围;CVE 是可靠的 daemon 终止条件,直接在生产入口试探会造成真实中断。
5.2 补丁验证要证明运行中的二进制已经改变
ipsec version 能打印用户态工具和 Libreswan 版本,是盘点起点。它仍可能来自新软件包,而旧 pluto 进程尚未重启;容器也可能更新 tag 却继续运行旧 digest 的 Pod。闭环需要同时记录软件包 NEVRA/DEB version、二进制 hash、进程启动时间和 /proc/<pid>/exe 指向。
厂商 backport 不一定把产品版本提升到 5.3.1。判断修复时应查发行版安全公告、package changelog 或源码补丁,确认重组函数使用 <=,并确认编译出来的 build 来自该 source revision。单纯比较 banner 会把固定的旧版本误报,也会把未合入补丁的私有 build 漏报。
升级后必须安排受控 daemon 重启。HA 部署先在备用节点安装并验证,转移新建连接,再处理原主节点;单节点环境应评估现有 SA 的重建时间、远程管理备用通道和业务峰谷。重启完成后用 ipsec status、ipsec trafficstatus 或设备等价接口核对关键连接,观察至少一个 rekey。
修复验收不应只运行 exploit-shaped case。证书链较长、NAT-T、MOBIKE、不同加密套件、IPv4/IPv6、主动与响应端两种角色都可能使用 fragmentation。保留一组实际业务 peer 的报文尺寸与分片数量基线,升级后比较成功率和延迟,才能证明一字符边界修正没有在厂商集成中引入互操作回归。
回移 patch 时还要核对函数签名与 logger API。4.6 以来的重组代码经历过返回值、fragment owner 和 message digest 引用方式变更;机械 cherry-pick 可能发生上下文偏移。最终检查点始终是同一个:在取得现有 SKF 后允许 roof 等于容量,且任何后续新 digest 解析仍先做容量判断。
软件物料清单可将 Libreswan 版本与 appliance firmware 映射起来,但运行暴露还需要连接配置。一个固定 build 可以安全处理分片;一个受影响 build 若所有 IKEv2 连接确实禁用 fragmentation,入口暂时受限。版本与配置是两列独立证据,不能互相代替。
6 2021 年重构怎样制造了四年多的脆弱窗口
CVE 公告给出的受影响起点是 4.6。对比 4.5 与后续历史,可以把这个版本边界落到一笔具体提交:1a4715ea4b8501da60cdd72828d8d8d6e10f8aeb,作者在 2021 年 9 月 18 日将分片解密与重组从 ikev2_decrypt_msg() 中拆出,改由上层状态包处理函数显式协调。
重构目标合理。旧函数同时负责判断 SK/SKF、逐片验证、重组、解密普通 SK 和返回布尔状态,职责较重。新代码引入 decrypt_v2_incoming_fragments(),让所有片先完成验证,再调用独立的 reassemble_v2_incoming_fragments()。控制流更清晰,也方便后台加密计算与 fragment lifecycle 管理。
问题发生在搬迁一个既有边界时。4.5 的 ikev2_reassemble_fragments() 在重组前写着:若 digest_roof >= elemsof(digest),记录 “packet contains too many payloads; discarded” 并返回 false。这个条件把 roof 30 当作无法继续解析内部 payload 的对端错误,进程保持运行。
1a4715ea4b 删除了这段可恢复分支,在新重组函数中加入 passert(md->digest_roof < elemsof(md->digest))。比较范围看似相同,失败语义已经从日志加返回变为全进程终止。与此同时,重构后的代码仍沿用原有 *skf = sk 原位覆盖,没有增加下一格写入需求。
这笔提交进入 4.6,之后一直保留到 5.3。版本历史和代码差异相互吻合,形成清楚的因果链:旧版知道满表是网络可达状态,并以协议错误处理;重构把它提升成内部断言;数组模型没有变化;攻击者因此获得远程 SIGABRT。漏洞不是 RFC 7383 新增功能当天产生,也不是 2025 年 unknown payload 保留改动首次创建。
6.1 历史提交改变了错误域,没有改变数据结构
重构审查常把注意力放在 happy path 是否等价:片段能否解密、明文是否按序复制、SK chain 是否恢复、fragment owner 是否释放。这些行为在 1a4715ea4b 中大体保留。真正丢失的是 failure domain:同一个容量条件由可恢复输入错误变成了不可恢复程序错误。
安全敏感 parser 需要把断言分为两类。纯内部结构损坏,如 chain 指针互相矛盾且外部输入无法单独制造,可以使用致命 invariant;由报文长度、元素数量、枚举值或顺序推导的状态,应尽量走可恢复拒绝。即使协议规范说发送者“不应”构造某种组合,网络对手仍会构造它。
这里还有一个典型的 count/index 语义漂移。旧分支的 >= 从“还需要为内部 payload 留槽位”出发,面对满表提前拒绝;新断言似乎把同一表达式解释成“当前结构必须尚有空位”。重组本身并不消费空位,所以这项 invariant 比函数需求更强。开发者看到固定数组时自然选择严格小于,却没有重新追踪对象复用。
后续提交 6a9aac807a89659c874beaaa5af2a0ba801a7c90 调整首片消息的重建方式,3f0b015374f5ce85cc2e99325ef80b69843e0ac4 与 30dcb882e8fe2a2fadbe5f448c93f879f6100aab 继续处理未加密外层 payload 的保留。它们让第一片语义更完整,也使边界组合更值得测试;致命的严格小于条件仍可追溯到 2021 年重构。
2025 年提交 6824359f 让主线不再彻底丢弃无法识别的 payload descriptor,公开回归因此可用 29 个 unknown 快速填表。这个变化扩大了主线的简洁触发构造,却不是官方影响范围起点。维护线在 unknown 分支的计数不同,依然被列为受影响,进一步证明根因位于独立的重组断言。
代码考古的价值在于确定应该修什么、测试什么。若把问题归因于 unknown payload,防御者可能尝试过滤未知类型;历史显示已识别外层载荷也能形成容量状态,真正修复必须恢复重组对合法 roof 的认识。若把问题归因于 fragmentation 设计,可能永久关闭业务需要的 RFC 7383;源码显示一字符边界即可保留功能。
6.2 回归测试把历史边界重新变成工程契约
修复合入由提交 52c1c2e039f5f7bb27d34069d2d2426fea93ec36 汇总,并加入 KVM 测试目录 testing/pluto/ikev2-44-cve-2026-12413-max-payloads。发起端使用 ipsec whack --impair add_unknown_v2_payload_to:IKE_AUTH 和 pad_with_unknown_v2_payloads:1 控制测试包形状。
测试生成器的相关支持由 46339ea9fd5dd4e27c322bf327fe564fdc5d3a4d 与 805814a35f22f5370deddf878ea8784f6621d083 完善。impairment 属于测试接口,不是生产配置,也不意味着普通 Libreswan 客户端会自然发送这种消息。它提供一条可重复、可审查的方式,把 parser 推到精确边界。
预期 transcript 中出现 INVALID_SYNTAX 是一项关键设计选择。测试若只确认“没有 core”,可能因消息在更早阶段被静默丢弃而通过,无法证明满表真正走过重组。看到对端协议拒绝,结合 debug 中的 fragment collection 和内部 decode,才能确认状态穿越了旧断言位置。
维护分支应有对应的 recognized-payload 边界用例。若下游只回移主线 unknown 测试,旧 parser 可能因为 unknown 不递增而从未抵达 roof 30,形成虚假的绿色结果。测试设计必须与目标源码版本的计数位置一致,固定 payload 类型、出现次数和 expected digest trace。
建议在 CI 中加入一个小型模型测试,把 capacity 设为更小值并枚举 0、capacity−1、capacity、capacity+1 四种状态。parser 写入、reassembly reuse 与 inner decode 分别声明自己的前置条件。这样的属性测试比单个 CVE 报文更能防止未来重构再次混淆 count 与 index。
历史检查还应进入代码评审清单:任何将 return false、notify 或 parser error 改成 passert 的 diff,都要回答该状态是否受网络字段影响;任何固定数组边界迁移,都要保留“刚好装满”用例;任何 descriptor reuse 重构,都要验证 owner、chain 和计数同时守恒。
7 从资产盘点到生产恢复的处置路径
第一步是建立运行资产表。字段至少包含节点与集群、管理责任人、公网或合作网络入口、UDP 500/4500 暴露、操作系统或 appliance firmware、Libreswan package/source revision、当前 pluto PID 与启动时间、HA 角色、连接配置来源、有效 IKE 版本、有效 fragmentation 值、关键 peer 数量和维护窗口。缺少任一关键字段的节点不能标记完成。
版本判定规则应具体到固定边界:上游 4.6、4.7、4.8、4.9、5.0、5.1、5.2、5.3 进入升级队列;上游 5.3.1 及更新版本满足项目修复条件。发行版 backport 以供应商安全公告列出的 fixed package 为准,同时保存 changelog 或 patch 证据。仅有 “5.x” 或 firmware 大版本无法支撑结论。
配置判定要使用展开后的有效值。ipsec readwriteconf --config /etc/ipsec.conf 可解析语法与 also= 继承,ipsec connectionstatus 可查看运行连接;受管理 appliance 应导出控制器生成的实际配置。搜索单一文件里的 fragmentation=no 不够,include、模板默认值和运行时加载都可能覆盖它。
默认配置 fragmentation=yes 意味着“文件里没写”通常仍是启用。只要一个受影响 daemon 下的 IKEv2 connection 允许 fragmentation,网络处理代码就仍在同一进程可达。多租户或多连接实例不能用某条低风险 profile 的设置替代全进程评估。
7.1 临时缓解必须带着互操作证据
Libreswan 给出的配置缓解是:业务不需要 IKEv2 fragmentation 时,在每一条 IKEv2 connection 上设置 fragmentation=no。这会阻止双方使用 RFC 7383,切断本 CVE 的重组入口。若连接确实需要分片,官方没有等价配置规避,应尽快升级或应用对应分支补丁。
判断“无需分片”不能凭当前隧道数量。证书认证的 IKE_AUTH 常因完整证书链、OCSP、多个 traffic selector、EAP payload 或厂商扩展超过路径可承载尺寸。NAT-T 还增加 UDP 封装开销。关闭后,小型 PSK peer 可能正常,大型证书 peer 在特定链更新或漫游网络上才超时。
变更前应从代表性 peer 采集 IKE_AUTH 报文尺寸、SKF 使用次数、路径 MTU 与重传。覆盖最长证书链、IPv6、移动网络、云安全组、NAT 设备和跨区域链路。若过去七至三十天已经观察到 SKF,直接关闭会创造已知业务回归;若没有观察到,也要做一次完整重新认证与 rekey 测试。
临时变更的 PRD 应写明对象列表、配置 diff、审批人、开始与结束时间、业务探针、失败阈值和回滚动作。例如:修改前导出 resolved configuration;逐个 connection 设置 no;重载或受控重启;在五分钟内由指定 peer 建立新 IKE SA 与 Child SA;连续十五分钟错误率不得高于基线两个标准差;失败即恢复配置并把节点隔离至仅可信源可达。
边界 ACL 可以作为补充。站点到站点 peer 若拥有稳定地址,可只允许已登记来源访问 UDP 500/4500;远程办公入口通常无法严格限定。ACL 保护的是网络可达性,NAT、共享出口、源地址变化和内部被入侵主机都会改变假设。它应有到期日期,不能让受影响版本长期留在库存中。
速率限制要以 IKE SA 建立和失败 exchange 为单位,避免只按 UDP packet 数。正常 fragmented IKE_AUTH 天然包含多包,粗暴 PPS 阈值会误伤。网关或前置设备可以限制单源半开放 IKE SA、异常高密度外层 payload 和短周期重复初始化,同时确保不同 NAT 用户不会被聚合成一个来源封禁。
7.2 升级、重启和回归要形成一张闭环工单
升级优先级按“可达性 × 受影响 build × 业务承载量 × 恢复难度”排序。公网单节点远程接入、共享多租户控制面和无带外管理的站点应最先处理;仅可信专线可达且具备快速 HA 切换的节点可以排在后续批次。所有 4.6 至 5.3 实例仍要给出明确完成日期。
安装阶段保存 package manager 输出、repository origin、签名验证、source package revision 与变更单号。源码构建需固定 commit、compiler flags、依赖与产物 hash。若使用官方补丁,确认 URL 路径中的 CVE 编号为 CVE-2026-12413;公告文本的 patch section 曾出现数字转置为 CVE-2026-21413 的链接文字,应以正确目录和实际补丁内容为准。
滚动升级先处理不承载新连接的节点。安装后停止旧进程,启动新二进制,核对 PID、exe target、启动日志和版本,再让少量测试 peer 接入。确认 IKE_SA_INIT、IKE_AUTH、Child SA、DPD、rekey、删除与重新建立均正常后,逐步恢复权重。节点间不要共享一个未经确认的旧镜像 digest。
单节点维护要准备可达的带外通道和回滚包。提前记录关键 connection 的 owner 与验证窗口,通知可能发生重认证;变更后先建立管理隧道之外的测试连接,再恢复业务。若回滚到受影响 build,立即恢复已验证的 fragmentation=no 或入口限制,并重新打开漏洞工单,不能把回滚视为处置完成。
功能回归包含实际 peer 矩阵,安全回归包含精确容量矩阵。后者至少验证 roof 29、roof 30、尝试第 31 项、错误片号、total 33、首片 NP 为 NONE、后续片 NP 非零、重复片、缺片、乱序与 ICV 错误。每项记录预期日志、协议 notify、PID 是否变化和正常控制隧道是否存活。
满表用例的验收结果写成四条:重组函数被执行;旧 assertion 文本没有出现;恶意 exchange 以 INVALID_SYNTAX 或目标分支等价的可恢复错误结束;pluto PID 与 service invocation 保持不变。若只满足其中三条,仍需检查测试是否真的抵达边界。
生产监控在变更后至少维持一个业务 rekey 周期。观察 IKE authentication failure、fragment receive/decrypt、message too many payloads、service restart、core、Child SA churn、认证后端 latency 和用户报障。长证书链 peer 若出现 IKE_AUTH 重传,优先检查临时 fragmentation 配置是否残留。
完成条件由资产 owner 与安全 owner 双方签署:运行 build 有固定证据;所有有效配置已采集;临时措施已撤销或带到期日;满表和业务回归通过;历史日志完成回看;监控规则上线;回滚包与证据归档。这样“已升级”才会变成可复查的安全状态。
8 观测、取证与最终判断
检测这项漏洞最可靠的方式,是把网络侧的异常片组与主机侧的进程终止放在同一时间窗。单独看到 fragmented IKE_AUTH 很常见,单独看到 pluto 重启也可能来自升级或其他缺陷;当一组已通过完整性验证、第一片外层载荷密集的 SKF 紧接着触发相同断言 stack,归因强度显著提高。
主机日志应检索 reassemble_v2_incoming_fragments、digest_roof、passert、SIGABRT、status 6、core dumped 和异常 restart。不同分支的源码行号会变化,函数与表达式比固定行号稳定。保留 journal 的 monotonic timestamp、boot ID、unit invocation ID、PID、build ID 和 coredump metadata。
core 可用时先在隔离分析机提取 backtrace、md->digest_roof、elemsof(md->digest)、SKF chain 地址、首片 header、fragment total/count 与 IKE SPIs。不要在生产 core 中公开私钥、证书私有材料或用户身份;根据组织数据处理规则限制访问并生成去敏摘要。
网络证据记录五元组、NAT-T 标记、initiator/responder SPI、exchange type、message ID、fragment number/total、外层 Next Payload 链、critical bit 和包长。受保护内容无需解密即可完成大部分结构关联,但外层 payload 是否计入 digest 仍取决于目标源码分支。PCAP 保存要遵守 VPN 元数据的敏感性要求。
若只有 restart 没有 packet capture,可从 IKE debug 日志、conntrack、flow telemetry 和上游防火墙日志重建来源与节奏。攻击者每次进程恢复后需要新建 IKE SA,因而可能出现重复 IKE_SA_INIT、短命 SPI 和相似 fragment total。证据不足时将结论标记为“与 CVE 行为一致”,不要把任何 SIGABRT 自动归入本漏洞。
8.1 事件响应应同时保护证据与恢复入口
确认正在被连续触发时,先保留一次完整样本和主机证据,再在上游按来源、目的节点或连接入口实施临时限制。若所有业务 peer 地址可枚举,快速切换到 allowlist;远程办公环境可启用半开放 SA 与 per-source 限速。不要反复手工重启而不改变入口,攻击者会在监听恢复后再次命中。
将新建连接引流到已修复节点,隔离受影响实例,核对 HA 备用节点版本。共享虚拟 IP 的集群要确保丢弃规则随流量路径生效;只在崩溃节点本机封禁可能让下一包被调度到另一个旧节点。云负载均衡、Anycast 和 ECMP 场景应逐层确认。
安装固定 build 并受控重启后,先用正常 peer 验证,再在实验环境重放经授权的边界夹具。比对 PID、journal 和协议结果,确认原先导致 crash 的边界流量已经变为可恢复拒绝。随后解除临时 ACL 时分阶段放量,继续监控相同来源与结构。
回看范围从当前进程启动前延伸到组织保留期内的最早受影响版本。检索重复 SIGABRT、短周期 service restart、未知原因的 VPN 波动和同期密集 IKE 分片。没有持久化能力的公开证据,仍需检查攻击窗口中因控制面中断产生的认证绕行、HA 故障、监控盲点与业务补偿操作。
事件报告应明确三层事实:源码证明的触发与进程终止;本环境观察到的版本、配置和日志;仍缺失的 PCAP、core 或 peer attribution。CVE 本身不能读取隧道明文或修改配置,控制面不可用可能诱发人工改路、临时放宽策略或 fail-open,这些后续行为要单独调查。
修复后若仍出现同一 stack,优先确认运行二进制是否真正更新、容器是否复用旧 layer、厂商 backport 是否作用于相同函数、core 是否来自旧 PID。若 stack 已变化,按新的 assertion 表达式重新分析,避免把所有 IKE 崩溃长期归到一个 CVE。
8.2 最终结论:这是错误域与边界语义共同造成的拒绝服务
CVE-2026-12413 的决定性事实已经连成完整链路。msg_digest 以 30 个固定 descriptor 保存一条消息的解析结果,roof 是半开区间末端;RFC 7383 让第一片在 SKF 前保留外层载荷;尚未通过身份认证的 initiator 已可为 IKE_AUTH 片组生成正确完整性标签;旧重组函数面对 roof 30 执行致命严格断言。
重组的真实动作是复用 SKF 槽位构造 SK,没有第 31 次写入。修复允许 roof 等于容量,随后内部 decoder 继续执行既有上限并返回 INVALID_SYNTAX。这条正常错误路径同时保住两个安全目标:不接受资源超限的消息,不因远程输入终止共享 daemon。
历史解释了漏洞为何从 4.6 开始。4.5 对满表记录并返回,2021 年重构把同一网络可达条件转成 passert;数据结构与原位覆盖没有同步改变。2025 年主线 unknown payload 记账使公开回归更易构造,维护线拥有不同 parser 形状,根因仍落在同一个重组不变量。
我们的相邻审计覆盖片号数组、最大片数、重组总长度与全部 roof 用法,没有建立第二个独立越界漏洞。这个结论受固定源码范围约束;设备 fork、修改过的最大输入、额外 descriptor append 或不同 allocator 需要重新验证。研究材料中的未确认空间被保留,不影响当前拒绝服务结论。
生产处置的最短可靠路径是升级至 5.3.1 或包含等效 backport 的厂商包,重启到新二进制,并用业务分片与 roof 30 两类用例共同验收。只有业务明确不依赖 RFC 7383 时,才在所有 IKEv2 connection 上暂时设置 fragmentation=no;需要分片的入口没有配置层替代修复。
这次缺陷留给协议工程的经验也很具体:count 可以等于 capacity,index 不能;资源上限由输入决定时,错误必须回到对端协议域;重构 failure path 时要保留失败的恢复级别;原位对象转换应把地址、owner、chain 与计数画在同一张图上。落实这些规则,比记住某一行补丁更能防止下一次满表退出。
最终验收可用一条简单判断收束:同一台网关在恶意边界输入后仍保持原 PID,返回可恢复协议错误,随后正常 peer 能建立并完成 rekey,同时运行 build 与配置证据可追溯。四项全部成立,CVE-2026-12413 的工程风险才真正关闭。
参考资料
- Libreswan:CVE-2026-12413 官方安全公告
- Libreswan:CVE-2026-12413 补丁与材料目录
- Libreswan:安全公告索引
- Libreswan:5.3.1 发布入口
- Libreswan 5.3.1 源码发布包
- CVE.org:CVE-2026-12413 记录
- NVD:CVSS、CWE 与 CISA SSVC 信息
- 维护分支修复提交 5c4369af
- 主开发线修复提交 6088d294
- 修复与 max-payload 回归合入提交 52c1c2e0
- 2021 年引入严格断言的重构提交 1a4715ea
- 首片重建后续提交 6a9aac80
- 主线保留未知 payload 描述符提交 6824359f
- 重组保留未加密 payload 提交 3f0b0153
- 分片消息纳入外层 payload 提交 30dcb882
- IKEv2 unknown payload impairment 提交 46339ea9
- 测试 payload padding 修正提交 805814a3
- 上游 CVE KVM 回归目录
- 回归发起端 impairment 脚本
- 回归预期 console 与 INVALID_SYNTAX 结果
- 固定源码 demux.h:PAYLIMIT、digest 与 digest_roof
- 固定源码 ikev2.c:外层与内部 payload decoder
- 固定源码 ikev2_message.c:收片、验证与重组
- 固定源码 ikev2_send.h:1-based fragment 数组
- 固定源码 passert.c:日志后调用 abort()
- Libreswan v4.5:可恢复满表检查的基线
- Libreswan v4.6:官方受影响起点
- Libreswan v5.3.1:首个上游修复 tag
- RFC 7383:IKEv2 Message Fragmentation
- RFC 7296:Internet Key Exchange Protocol Version 2
- IANA:IKEv2 参数与 Payload Type 注册表
- Debian Security Tracker:CVE-2026-12413
- CWE-193:Off-by-one Error
- CWE-617:Reachable Assertion
- Libreswan ipsec.conf:fragmentation 配置语义
- Libreswan ipsec readwriteconf:展开与验证配置
- Libreswan ipsec connectionstatus:运行连接盘点
研究记录
9证据、对象与来源
下面保留本文实际使用的标识、时间和原始材料,便于继续调查。
9.1研究对象
报告涉及的产品、行为者、技术、受影响对象和控制点。
Libreswan IKEv2 分片重组的预认证可达断言
2021 年重构后到 5.3 的上游版本范围
2026 年 6 月 24 日发布的首个上游固定版本
允许 digest_roof 等于 30 的维护线与主线提交
把可恢复满表返回改成致命断言的 2021 年重构
复用 SKF descriptor 构建 SK 的分片重组函数
合法满容量计数,数组有效下标仍为 0 至 29
只适用于已验证无需 RFC 7383 的全部 IKEv2 connection
9.2事件时间
- 重构引入致命边界
提交 1a4715ea 将旧版安全返回替换为严格小于断言,随后进入 4.6。
- 问题报告给项目
Libreswan 收到分片满载导致 daemon 退出的私下报告。
- 修复与 5.3.1 发布
项目发布维护线与主线修复、固定版本和安全公告。
- CVE 记录公开
CNA 公布受影响范围、网络可达性与拒绝服务影响。
- SOSEC 完成扩展源码复核
固定到 5.3.1 源码,打通协议、函数、历史、回归与生产处置链。
9.3来源与材料
- Libreswan:CVE-2026-12413 官方安全公告https://libreswan.org/security/CVE-2026-12413/CVE-2026-12413.txt
- Libreswan:CVE-2026-12413 补丁与材料目录https://libreswan.org/security/CVE-2026-12413/
- Libreswan:安全公告索引https://libreswan.org/security/
- Libreswan:5.3.1 发布入口https://libreswan.org/
- Libreswan 5.3.1 源码发布包https://download.libreswan.org/libreswan-5.3.1.tar.gz
- CVE.org:CVE-2026-12413 记录https://www.cve.org/CVERecord?id=CVE-2026-12413
- NVD:CVSS、CWE 与 CISA SSVC 信息https://nvd.nist.gov/vuln/detail/CVE-2026-12413
- 维护分支修复提交 5c4369afhttps://github.com/libreswan/libreswan/commit/5c4369afcfe95a924ee0a96a0b29a7204dc40504
- 主开发线修复提交 6088d294https://github.com/libreswan/libreswan/commit/6088d294b8a6448df1611b98d778d85482646586
- 修复与 max-payload 回归合入提交 52c1c2e0https://github.com/libreswan/libreswan/commit/52c1c2e039f5f7bb27d34069d2d2426fea93ec36
- 2021 年引入严格断言的重构提交 1a4715eahttps://github.com/libreswan/libreswan/commit/1a4715ea4b8501da60cdd72828d8d8d6e10f8aeb
- 首片重建后续提交 6a9aac80https://github.com/libreswan/libreswan/commit/6a9aac807a89659c874beaaa5af2a0ba801a7c90
- 主线保留未知 payload 描述符提交 6824359fhttps://github.com/libreswan/libreswan/commit/6824359f416a1eba0fabc0f19d5fe9267338bf3b
- 重组保留未加密 payload 提交 3f0b0153https://github.com/libreswan/libreswan/commit/3f0b015374f5ce85cc2e99325ef80b69843e0ac4
- 分片消息纳入外层 payload 提交 30dcb882https://github.com/libreswan/libreswan/commit/30dcb882e8fe2a2fadbe5f448c93f879f6100aab
- IKEv2 unknown payload impairment 提交 46339ea9https://github.com/libreswan/libreswan/commit/46339ea9fd5dd4e27c322bf327fe564fdc5d3a4d
- 测试 payload padding 修正提交 805814a3https://github.com/libreswan/libreswan/commit/805814a35f22f5370deddf878ea8784f6621d083
- 上游 CVE KVM 回归目录https://github.com/libreswan/libreswan/tree/v5.3.1/testing/pluto/ikev2-44-cve-2026-12413-max-payloads
- 回归发起端 impairment 脚本https://github.com/libreswan/libreswan/blob/v5.3.1/testing/pluto/ikev2-44-cve-2026-12413-max-payloads/westrun.sh
- 回归预期 console 与 INVALID_SYNTAX 结果https://github.com/libreswan/libreswan/blob/v5.3.1/testing/pluto/ikev2-44-cve-2026-12413-max-payloads/west.console.txt
- 固定源码 demux.h:PAYLIMIT、digest 与 digest_roofhttps://github.com/libreswan/libreswan/blob/de24519eb4ff49803a21d75e880c87834cf33beb/programs/pluto/demux.h
- 固定源码 ikev2.c:外层与内部 payload decoderhttps://github.com/libreswan/libreswan/blob/de24519eb4ff49803a21d75e880c87834cf33beb/programs/pluto/ikev2.c
- 固定源码 ikev2_message.c:收片、验证与重组https://github.com/libreswan/libreswan/blob/de24519eb4ff49803a21d75e880c87834cf33beb/programs/pluto/ikev2_message.c
- 固定源码 ikev2_send.h:1-based fragment 数组https://github.com/libreswan/libreswan/blob/de24519eb4ff49803a21d75e880c87834cf33beb/programs/pluto/ikev2_send.h
- 固定源码 passert.c:日志后调用 abort()https://github.com/libreswan/libreswan/blob/de24519eb4ff49803a21d75e880c87834cf33beb/lib/libswan/passert.c
- Libreswan v4.5:可恢复满表检查的基线https://github.com/libreswan/libreswan/tree/v4.5
- Libreswan v4.6:官方受影响起点https://github.com/libreswan/libreswan/tree/v4.6
- Libreswan v5.3.1:首个上游修复 taghttps://github.com/libreswan/libreswan/tree/v5.3.1
- RFC 7383:IKEv2 Message Fragmentationhttps://www.rfc-editor.org/rfc/rfc7383
- RFC 7296:Internet Key Exchange Protocol Version 2https://www.rfc-editor.org/rfc/rfc7296
- IANA:IKEv2 参数与 Payload Type 注册表https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml
- Debian Security Tracker:CVE-2026-12413https://security-tracker.debian.org/tracker/CVE-2026-12413
- CWE-193:Off-by-one Errorhttps://cwe.mitre.org/data/definitions/193.html
- CWE-617:Reachable Assertionhttps://cwe.mitre.org/data/definitions/617.html
- Libreswan ipsec.conf:fragmentation 配置语义https://libreswan.org/man/ipsec.conf.5.html
- Libreswan ipsec readwriteconf:展开与验证配置https://libreswan.org/man/ipsec-readwriteconf.8.html
- Libreswan ipsec connectionstatus:运行连接盘点https://libreswan.org/man/ipsec-connectionstatus.8.html