在 Pwn2Own Automotive 2025 上,法国安全团队 Synacktiv 演示了一条从握住充电手柄到完全远程代码执行的攻击链,最终获得 CVE-2025-8321(ZDI-25-712)。这个漏洞的核心并非一个孤立的缓冲区溢出,而是一套完整的固件降级再利用链:在充电线缆的单线 CAN 总线上完成认证、降级固件、触发调试接口。Tesla 随后在固件 24.44.3 中引入了防回滚 ratchet,试图阻止这种降级攻击。然而 Synacktiv 在 2026 年 5 月披露的 Part 2 研究表明,这道防线同样可以被绕过 —— 原因在于安全检查被放在了错误的位置。
攻击入口:充电手柄等同于诊断端口
Tesla 第三代 Wall Connector 使用单线 CAN(SWCAN,33.3 kbps)作为车辆与充电桩之间的诊断通信媒介。这条总线承载在 Control Pilot 导线上 —— 也就是那根在充电握手时输出 1 kHz PWM 信号的导线。一旦握手完成、PWM 让位给 CAN 帧,任何人握住这个充电手柄的连接器就物理上接入了一条私有诊断网络,等同于把 OBD-II 诊断接口延伸到了十几米外的充电枪头。
攻击者需要的硬件装备并不复杂:一块 USB-to-CAN 适配器(将高速 CAN 收发器替换为 NCV7356 单线器件)、一块运行 IEC 61851 握手仿真的微控制器,以及用来维持 100 ms 心跳帧的固件。软件层面则围绕 ISO 14229 统一诊断服务(UDS)展开:打开 UDS 会话(Session 2)、通过 Security Access(Level 5)认证。关键发现是 Tesla 使用的种子 - 密钥算法极其简单 —— 仅将 16 字节种子与固定值 0x35 做 XOR 运算。这一认证绕过使得攻击者获得了 UDS 扩展诊断模式的完全访问权限,从而可以对内部嵌入式模块 AW-CU300 的闪存执行读写操作。
Pwn2Own 原版攻击链:降级固件的完整利用路径
Synacktiv 在 2025 年 Pwn2Own 使用的攻击链由三个阶段串联而成。首先,通过 UDS 读取 0x102 数据标识符将充电桩存储的 Wi-Fi SSID 和 PSK 以明文形式泄漏,攻击者由此从充电桩内部网络横向移动到用户家庭局域网。其次,将旧版固件 0.8.58 写入被动槽位 —— 该版本包含一个以 Telnet 形式存在的调试 shell,监听在可从网络直接访问的端口上。最后,通过该 shell 触发参数解析器中的单字节缓冲区溢出,实现任意代码执行。
值得注意的是,固件 0.8.58 的 Telnet shell 本身运行在 RWX(可读可写可执行)内存页上,没有任何 DEP(数据执行保护)或 ASLR(地址空间布局随机化)加持,输入过滤器仅过滤了换行符 \r。一旦通过 UDS 将此固件烧录进设备,攻击者只需 telnet 连接即可获得 root shell,进而操控继电器和 LED。Tesla 最初将这个调试 shell 留在生产固件中被认为是疏漏,但 Synacktiv 的 Part 1 论文指出这可能是为了降低远程诊断成本而有意保留 —— 无论动机如何,它直接成为了攻击者的特权入口。
Tesla 的补丁:应用层防回滚 ratchet
Tesla 在 24.44.3 中引入的修复思路是引入安全 ratchet 机制。该机制要求新固件在特定段中携带版本描述符(VRSN 字段记录主版本号和次版本号)和 ratchet 值(VRS2 字段中的某个字节)。每当成功的固件更新通过 UDS routine 0x201 提交时,更新器会调用 check_image_and_antidowngrade() 函数重新计算固件段 CRC,然后调用 verify_firmware_segments_platform() 从闪存中读取当前存储的 ratchet 值与之比较 —— 只有当新固件的 ratchet 大于等于当前值时,才允许更新操作继续。
从代码逻辑看,verify_firmware_segments_platform() 函数的比较条件为:
if (current_ratchet <= firmware_ratchet || !call_psm_wrapper(...))
return 0; // accepted
log("Failure: Security ratchet downgrade prevented %d < %d",
firmware_ratchet, current_ratchet);
return -1;
如果比较失败,更新器会立即擦除被动槽位的内容并返回错误。在 Synacktiv 的测试中,向运行 24.44.3 的充电桩发送旧版 0.8.58 并调用 routine 0x201 会立即触发上述日志并擦除固件。这套机制本身设计合理,但有一个致命的架构缺陷 ——ratchet 检查完全由更新器的应用层代码实施,bootloader 对此一无所知。
绕过的根本原因:检查位置错误
Synacktiv 在 Part 2 中揭示的绕过精妙之处在于,它利用了固件更新流程中三个独立操作的时序漏洞。仔细分析 switch_to_new_firmware()(即 UDS routine 0x201 的实现)可以发现:该函数在验证固件签名和 ratchet 通过后,会调用 part_write_layout() 来更新分区表。分区表的核心作用是递增目标槽位的 gen_level 计数器 —— 当设备重启时,bootloader 读取分区表并选择 gen_level 最高的槽位作为下一个启动分区。
然而,part_write_layout() 函数的行为非常明确:它只修改分区表中的元数据(gen_level 计数器),完全不触碰槽位本身的固件内容。这意味着,一旦更新器通过 ratchet 检查并写入了分区表,该槽位就获得了最高 gen_level,但槽位内的二进制数据仍然可以被后续操作覆写。
Synacktiv 发现的绕过步骤如下。第一步,向被动槽位写入一个有效的、已签名的新版固件(24.44.3),然后调用 routine 0x201——ratchet 检查通过,分区表被更新,该槽位获得最高 gen_level。第二步,不重启,再次调用 routine 0xFF00 准备被动槽位。关键细节在于 prepare_passive_slot() 函数中读取 g_boot_flags 的逻辑:该标志在设备启动时设置,代表本次启动时使用的是哪个物理槽位,在整个 UDS 会话期间保持不变。因此 0xFF00 会再次选择同一个物理槽位作为目标,执行擦除操作 ——part_erase() 将该槽位中的新版固件数据彻底清除,但分区表中该槽位的 gen_level 保持不变。第三步,向刚刚被擦除的同一槽位写入旧版固件 0.8.58(该固件仍然具有有效签名,因为旧版固件本身确实经过 Tesla 签名)。第四步,跳过 routine 0x201,直接调用 routine 0x202 重启。
设备重启后,bootloader 读取分区表,选择 gen_level 最高的槽位 —— 正是我们之前操作过的那个槽位。Bootloader 对固件内容执行标准校验:检查 magic header(SBFH)、逐段 CRC32、RSA 签名与 keystore 中公钥的匹配。这些校验全部通过,因为旧版固件 0.8.58 确实拥有有效签名。随后 bootrom 将控制权转交给 0.8.58 的入口点。整个过程中,防回滚 ratchet 检查从未被执行 —— 因为 ratchet 检查代码只存在于 UDS 更新器的 switch_to_new_firmware() 函数中,而我们在绕过中跳过了这个函数的调用。攻击者重新获得了包含调试 shell 的旧版固件,原有的完整攻击链再次成立。
为什么 bootloader 必须承担 ratchet 检查职责
Synacktiv 在 Part 2 论文中明确指出了三条可行修复路径,每条都指向同一个核心原则:ratchet 验证必须从应用层下沉到底层。
第一种方案是在 bootloader 中实现 ratchet 检查。当 bootloader 在启动时选择要执行的固件槽位时,它应当读取该槽位固件中的 ratchet 值,并与持久化存储中保存的当前 ratchet 进行比较。这要求将 VRSN/VRS2 段信息纳入安全启动的验证范围内 ——bootloader 需要能够解析固件格式、提取版本元数据,而不仅仅是验证签名。如果当前运行的 ratchet 值大于待启动固件的 ratchet 值,则拒绝跳转并进入恢复模式。这种方案从架构上消除了 "应用层检查可以被绕过" 的问题。
第二种方案是让 routine 0xFF00 在擦除槽位时同时使该槽位在分区表中失效。具体做法是在 prepare_passive_slot() 执行擦除之前,先将对应槽位的 gen_level 降低或将其标记为不可启动。这样即使后续有固件被写入该槽位,没有被提升的 gen_level 也不会在重启时被 bootloader 选中。这需要修改分区表操作逻辑,使擦除和布局更新成为一个原子操作。
第三种方案是从会话级别禁止重复更新。在 routine 0x201 成功执行后,更新器应当设置一个会话标志位(或更新持久化存储中的状态),表明当前会话已完成一次成功的固件提交。在此之后,任何对 0xFF00 的再次调用都应当被拒绝,直到设备下一次完整重启。这相当于在协议层面实现了 "One update per boot" 的约束,使攻击者无法在不清除分区表的情况下重新准备槽位。
无论选择哪条路径,教训都是一样的:安全检查的有效性取决于它是否运行在攻击者最难干预的层级。当 ratchet 验证仅存在于可被跳过和重入的应用层更新逻辑中时,它实际上只是一个 "方便之门"—— 对遵守协议的攻击者有约束力,对愿意操纵时序的攻击者则形同虚设。真正的防御必须将版本约束嵌入到不可绕过的信任根中。
对 EVSE 行业的更深层启示
这起案例折射出电动汽车供电设备(EVSE)安全领域的一个系统性盲点:充电桩被视为 "配电附件" 而非 "安全关键边缘设备"。现实中,Wall Connector 被部署在家庭车库、企业停车场和公共充电站,它同时连接高电压电网、汽车电池组和用户家庭局域网。一旦被攻陷,它既是网络渗透的跳板,也是 CAN 总线层面的车辆入口点。
Synacktiv 将整个攻击链映射到汽车威胁矩阵(ATM)时发现,每个攻击阶段都能在 ATM 中找到对应条目:从 "意外车辆网络消息注入"(ATM-T0071)、"ECU 凭据窃取"(ATM-T0039)到 "重新编程 ECU 实现横向移动"(ATM-T0054)。这表明 EV 充电基础设施的安全风险并非模糊的 "IoT 安全" 问题,而是与车内网络、CAN 总线安全和远程诊断访问直接关联的跨域威胁。
对于部署 Tesla Wall Connector 或其他联网充电桩的终端用户而言,最实际的缓解手段是依赖 Tesla 的自动 OTA 更新机制 —— 只要充电桩保持联网,Tesla 可以在数周内将修复固件推送到大多数设备,大幅缩短暴露窗口。但在无法立即更新的场景下,网络层的访问控制显得尤为重要:将充电桩放置在独立 VLAN 中、限制其对内网服务的访问范围,可以在一定程度上遏制降级成功后的横向移动。
资料来源
- Synacktiv, "Exploiting the Tesla Wall Connector from its charge port connector – Part 2: Bypassing the anti-downgrade"(https://www.synacktiv.com/en/publications/exploiting-the-tesla-wall-connector-from-its-charge-port-connector-part-2-bypassing)
- VicOne, "From Pwn2Own Automotive 2025: Unpacking the Tesla Wall Connector Exploit Chain and Its Broader Cybersecurity Implication"(https://vicone.com/blog/from-pwn2own-automotive-2025-unpacking-the-tesla-wall-connector-exploit-chain-and-its-broader-cybersecurity-implication)
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。