Jiayun's Blog

探索与分享

📌 好文共赏 | Editor’s Pick 原文:Exploiting the Tesla Wall Connector from its charge port connector - Part 2: bypassing the anti-downgrade | 作者:David Berard(Synacktiv) | 发布:2026-05-12 | 阅读时长:约 18 分钟 多模评分:Opus 9.0 / Sonnet 8.8 / Gemini 8.9(综合 8.9/10) 一句话推荐:当 Tesla 把"防降级闸门"焊在更新例程里,攻击者只要让闸门在错误的时刻关上,整条 Pwn2Own 攻击链就能原封不动地穿过它。


1. 为什么值得读

这是一篇典型的"硬件 + 协议 + 状态机推理"三位一体的逆向手记。Synacktiv 的 David Berard 在 2025 年 1 月的 Pwn2Own Automotive 上,凭借一个朴素的事实——Tesla Wall Connector Gen 3 没有任何防降级机制——把一个被签名保护的现代设备打到了 telnet root shell。比赛后 Tesla 火速发布 24.44.3 固件,给更新流程加上"安全棘轮(security ratchet)":每个固件镜像都带一个版本号 VRS2,更新器只接受不低于设备当前棘轮的镜像,否则就地擦掉。听上去,原本的攻击链应该终结了。

文章的可贵之处,是它不靠一个新的内存损坏漏洞,也不靠 0day 算法弱点,只靠对状态机的精细阅读,把这层硬性闸门绕了过去。具体逻辑可以浓缩成一句话:

既然 anti-downgrade 只在 0x201 这一条 UDS 路由里执行,而 bootloader 只看分区表上的 gen_level——那就先用一个合法新固件把分区表写进去,再用 0xFF00 把这片物理 slot 擦掉,然后把旧固件灌回去,最后跳过 0x201 直接重启。bootloader 不知道这块 slot 被擦过又被重写,它只看到 gen_level 最高、签名合法的镜像,于是按部就班地把旧固件拉起来。

这种"防御只在某条单一路径上生效,那就走旁路"的攻击哲学,几乎是嵌入式安全里反复出现的母题。但 Berard 用一篇 14 分钟的文章把它讲得极其干净:IDA 反编译截图、switch_to_new_firmware() 的伪代码、part_write_layout() 里 gen_level 自增的两行、SWCAN 上 33.3 kbps 的传输节奏——每一处细节都恰好够你"自己推一遍",没有多余的演讲腔。

另一个让人会心一笑的点:作者在开篇就写下一句反潮流的注脚——

原文:“This is one of those vulnerabilities you find by hand, with a coffee, an IDA window, and zero help from a language model. Do you remember those old good days?”

在 Mythos、Pike 这些 AI 漏洞猎手刚刚把 curl 弄得鸡飞狗跳Anthropic 的 curl 误报事件),以及 Calif 和 Mythos 一周内绕过 Apple MIE 的当下,这句话像一根温柔的反向坐标轴:最优雅的漏洞,依旧来自一个安静坐着、看着 IDA 反编译、把状态机推演到最后一步的工程师

2. 背景:Pwn2Own Automotive 与那条充电线

Pwn2Own 是 ZDI 主办的全球顶级漏洞演示赛,2024 年开辟了"汽车专场",把车载信息娱乐系统、充电桩、ECU 都放进了打靶清单。2025 年 1 月东京站,Synacktiv 把 Tesla Wall Connector Gen 3——也就是普通用户在车库里装的家用充电桩——当成目标。

他们选了一个非常"反直觉"的攻击面:充电连接器本身。Tesla 车辆有一个鲜为人知的能力:通过充电线,用 Single-Wire CAN(SWCAN)协议升级 Wall Connector 的固件。这是 J1772 北美标准里的"扩展用法"——Proximity Pilot 和 Control Pilot 两根线,本来只用来识别充电缆容量、协商电流,但被 Tesla 拿来跑了一条 UDS(Unified Diagnostic Services,ISO 14229)通道。Berard 团队做了一台"假 Tesla"——一块 GPIO + SWCAN 收发器 + UDS 软件栈——插进 Wall Connector 的充电口,就能像一辆合法的 Tesla 一样发起诊断会话。

在 Pwn2Own 现场,他们把一份旧版本(0.8.58)的合法签名固件写进 passive slot,重启,进入熟悉的 telnet 调试 shell,再触发原固件里的一个 argument parser 缓冲区溢出——root。整条链子的"关键脆弱点"不是签名校验弱(Tesla 用了 RSA 严格校验),而是**“更新流程根本不检查新版本是否高于当前版本”**。在那个时间点,签名只能告诉你"这是一个合法的 Tesla 固件",而不能告诉你"这是一个不该再被使用的 Tesla 固件"。

修复来得很快。Tesla 在 24.44.3 里给 switch_to_new_firmware() 加了一层 check_image_and_antidowngrade()。固件段表里多了一个 VRS2 描述符,里面带着 “ratchet” 字节;设备端在 PSM(Persistent Storage Manager)里维护一个 current_ratchet。新镜像的 ratchet 必须 ≥ 设备当前的 ratchet,否则更新被拒绝、passive slot 立即擦掉,错误日志 Security ratchet downgrade prevented 写进串口。看上去万无一失。

如果你读到这里就停,会以为故事结束。但好文章的"裂缝"就在这种"看上去万无一失"里。

3. 关键观察:防降级只在更新器里,bootloader 只看分区表

Berard 用反编译比对的方式找到了那块"被 ratchet 保护"的代码——它只在 UDS 路由 0x201 内部生效。整个安全检查链条是这样的:

1
2
3
4
5
6
7
UDS routine 0x201
  └─ switch_to_new_firmware()
       └─ check_signature()                ← RSA 签名
       └─ check_image_and_antidowngrade()  ← 新增的 ratchet 闸门
            └─ verify_firmware_segments_platform()
                 └─ 比较 firmware_ratchet 与 PSM 里的 current_ratchet
       └─ part_write_layout()              ← 真正翻转分区表的动作

接下来的反向问题是:bootloader 在乎 ratchet 吗?

Berard 从一台被原版 Pwn2Own 攻击链先 root 了的 charger 里 dump 出 boot2 —— 这块 bootloader 是出厂烧在 flash 里、Tesla OTA 从不下发的固件。他在 IDA 里一行行翻:

  • 检查 SBFH 魔术头 ✓
  • 检查每个 segment 的 CRC32 ✓
  • 用 keystore 里的 RSA key 校验签名 ✓
  • 检查 ratchet?没有。

这就是裂缝。原文给了一句话总结:

原文:“the anti-downgrade is enforced exclusively by one piece of code, switch_to_new_firmware(), at one moment: when routine 0x201 is called.”

整个设备里,只有一段代码、一个时刻在执行防降级检查。

这种"单点防御"在嵌入式系统里相当普遍——bootloader 一旦固化,就很难再加新功能;OTA 路径走的是高层应用,那里加什么补丁都容易。但代价是:只要让"那个时刻"不在攻击路径上发生,整条防御就形同虚设。Berard 的下一步,就是去寻找这条"不经过 0x201“的合法升级路径。

4. Tesla 自己留下的"两步法"漏洞:分区表与物理 slot 的解耦

Wall Connector 用经典的 A/B slot 设计:active 是当前运行的固件,passive 是升级目标,更新成功后翻转。关键问题是:怎么决定哪一块是 passive?怎么决定下次启动跑哪一块?

Berard 把这两个动作拆得很干净:

(1) 谁是 passive?看 g_boot_flags

prepare_passive_slot()0xFF00 路由里被调用。它根据全局变量 g_boot_flags 决定"哪一块物理 slot 是 passive”——而 g_boot_flags 在系统启动时一次性写入,整个会话过程中永远不变

1
2
3
4
if ((g_boot_flags & 3) != 0)    // 我们从 slot 1 启动?
    f2 = f1;                    // 那 slot 0 就是 passive
passive_firmware = f2;
// 然后 part_erase() 擦掉这块物理 slot

(2) 下次启动跑哪一块?看分区表里的 gen_level

part_write_layout() 不动 firmware 数据,只重写分区表(一个 4 KiB 区域),把目标 slot 的 gen_level 增 1。bootloader 启动时挑 gen_level 最高的 slot 跳进去。

这两件事看似紧耦合,其实彼此独立:

  • 物理擦除由 0xFF00 触发,依据 g_boot_flags
  • 分区表由 0x201 触发,依据传进来的镜像。

只要在一个 UDS 会话里连续调用两次 0xFF00,再各塞一份不同固件——它们指向的物理 slot 是同一块(因为 g_boot_flags 不变),但只有第一次 0x201 把分区表写了。第二次擦除把"合法新固件"擦回了零,但分区表上 gen_level 已经指向这块 slot

5. 攻击编排:一句话画出整条状态机

Berard 给出的攻击编排极简,可以一字不落地读:

  1. 第一轮(写合法新固件):发 0xFF00 准备 passive slot → 写 24.44.3 的合法新固件 → 发 0x201。这一步通过所有 ratchet 检查,分区表被写入,gen_level 升级,slot 指针指向"这块"物理 slot
  2. 第二轮(擦回旧固件):在不重启的前提下再次发 0xFF00。因为 g_boot_flags 没变,同一块物理 slot 被擦掉。但分区表没动
  3. 把 0.8.58 的旧固件灌进去——它有合法的 RSA 签名,但 ratchet 是 0。
  4. 跳过 0x201——这正是这次攻击不被 ratchet 拦截的关键。直接调 0x202 重启。

启动时,bootloader 读分区表,挑 gen_level 最高的 slot——刚刚被第二轮重写的那块——验证 RSA 签名(仍然有效,它是合法 Tesla 签名)、CRC(重新计算)、魔术头 SBFH,然后跳进去。

整个过程,ratchet 检查从未对这块物理内容生效。原 Pwn2Own 链子里的 telnet shell、参数解析 buffer overflow,一个不少地原样运作。

唯一的代价是时间:SWCAN 跑在 33.3 kbps 上,发两份完整固件大约 30 分钟。原文写道:“Total run time is roughly 30 minutes on the 33.3 kbps SWCAN bus: twice the original Pwn2Own timing."(一个充电桩的家用攻击,要求攻击者把假 Tesla 插着半小时——这本身又是另一个有意思的物理威胁模型问题。)

6. 修复建议:把单点变成多点

Berard 在结论里给出三条修复建议,每一条都对应"裂缝的一个具体几何位置”:

  1. 把 ratchet 检查也放进 bootloader:让 anti-downgrade 不止在更新器里生效。这是最干净的方案,但代价是 bootloader 复杂度上升,一旦 bootloader 出 bug 不能 OTA 修复。
  2. 0xFF00 在擦除时一并失效分区表条目:把"擦内容"和"擦元数据"绑在一起。这是最小改动方案,但 layout 数据丢失也意味着回滚能力丢失。
  3. 0x201 成功后强制重启 / 不允许同会话开第二次更新:从协议层面切断这条状态机路径。最简单,但要求所有客户端配合。

值得注意的是:Tesla 实际收到这份报告并已在更早的固件中修复了——Berard 现在才发布是因为他遵守了厂商通报流程(responsible disclosure)。Wall Connector 大多数装在家庭或小型企业网络里,一旦被拿下,它就成了"内网的一只脚"。Tesla 强制 OTA 的部署模式让修复一般几周内能覆盖大多数在线设备,但离线的 Wall Connector 仍是攻击窗口

7. 这一漏洞的范式价值:状态机视角的安全研究

很多嵌入式安全研究关注两类问题:密码学弱点(密钥可猜、签名可伪)和内存破坏(缓冲区溢出、UAF)。Berard 这篇文章的范式属于第三类,也是最容易被忽视的一类——状态机间的耦合错位

它的特征是:

  • 每一段代码单独看都是正确的switch_to_new_firmware() 严格执行 ratchet 检查,part_write_layout() 老老实实写 gen_level,prepare_passive_slot() 也只是简单看一下 g_boot_flags
  • 但它们的调用顺序、所依赖的全局变量、所操作的物理资源之间存在一条没被设计者考虑到的可达路径。
  • 测试用例几乎不可能覆盖到这种路径,因为单元测试看不见"两次连续 0xFF00“是异常用法,集成测试又默认按"合法升级"流程走一遍。

熟悉协议设计的同学应该意识到:这其实和 Cloudflare 的 14ms QUIC 死亡螺旋同一类问题——多个本身正确的子系统(CUBIC、QUIC、Linux kernel)在某个交叉点形成了一种没被任何一方预料到的反馈。我之前写过的 DuckDB Quack 协议 也强调过类似的论点:协议设计的真正难点不在单一动作的正确性,而在动作组合的封闭性

如果你做嵌入式 / IoT / OTA 系统开发,这篇文章的实际教益至少有三层:

  • 把安全检查放进 trust anchor。Tesla 的 bootloader 是它的最终 trust anchor——既然 ratchet 是安全闸门,它就应该在那里复检,而不是依赖一个上层 OTA 路径。
  • 状态变量要有寿命g_boot_flags 应当随着分区表的翻转更新,而不是只在启动时锁死。
  • 写"撤销操作"的人要思考"撤销有没有恢复元数据”part_erase() 擦内容但不擦 layout,本质上是一种"有副作用的撤销不完整"。

8. 延伸阅读图谱

Synacktiv & David Berard 相关代表作

相关研究 / 对比阅读

反向 / 互补观点

站内相关:跨主题映射

9. 编辑延伸思考:从"补丁的形状"看安全工程的复杂性

Berard 这次的发现,本质上揭示的是**安全补丁的"形状”**问题。

一个高质量的补丁应该满足三个属性:

  1. 完整性(Completeness):覆盖所有可达路径,不只是已被报告的那一条。
  2. 不变性(Invariance):在任何上下文中,受保护的属性都成立,而不只是在"正常"上下文中。
  3. 持久性(Persistence):跨重启、跨会话、跨 OTA 仍然成立。

Tesla 24.44.3 的 anti-downgrade 补丁在 (1) 和 (3) 上都不完整:

  • 它**漏掉了"在同一会话里物理 slot 可以被再次擦除"**这条边路(违反完整性);
  • 只保护 0x201 路径,没保护"分区表 + bootloader" 这条无 0x201 路径(违反不变性)。

这种"补丁形状不全"的现象在大型项目里极其常见。CVE-2025-33073 的 Windows 反射攻击补丁——Synacktiv 自己在 4 月的两篇 SYSTEM Shell 文章里证明它是不完整的——是另一个完美样本。Linux Kernel 历史上无数次出现"在系统调用 A 处加了检查、忘了系统调用 B 也能到这条路径"的案例。OpenSSL Heartbleed 之后,业界做了多次"全局变量审计",但跨二进制、跨模块的副作用拼图依然难拼。

更具反思价值的是:这种漏洞是"防御机制有,但失效",比"完全没有防御"更危险。后者会让用户警惕;前者会让用户产生"我已经安全了"的虚假安心感。Berard 这种工作的最大社会价值,是戳破"已修复"标签,把它还原成"在某条具体路径上被修复"。

另一层值得说的,是自动化 vs. 手工的辩证。这篇文章发布的同一周,Mythos Preview 帮助 Calif 团队在 5 天内绕过了 Apple MIE——AI 加速的安全研究第一次真切地拿到了"巨型工程级硬件防御"的头皮。但 Berard 的这个 Tesla 漏洞,是没有任何 AI 能在当前架构下自动发现的类型——它要求理解:

  • UDS 协议语义;
  • 厂商定义的私有 routine ID 与状态机;
  • 物理 slot / 分区表的耦合关系;
  • bootloader 与更新器的信任边界划分。

这些信息没有任何公开的标注训练集。哪怕你把 Synacktiv 自己的 Pike LLM agent 放上去,它能帮你分析二进制的某个函数,但让它"提出’连续两次 0xFF00 会不会让分区表与物理内容解耦’这个假设"——这是当前模型能力之外的事。

所以本文真正的好处,不是它讲了一个漏洞,而是它演示了在 AI 加速安全研究的时代,“人的部分"具体是什么:是阅读状态机、是质疑文档、是在头脑里把若干个例程拼成一张图、是看到补丁后不假设它完整。这是任何想做安全研究的工程师都应该练习的核心动作。

10. 配套资料导览

本文目录下附带四份配套资料,建议照下面顺序读:

  1. cover.svg —— 封面图,深色背景 + Tesla 充电桩与 ratchet 闸门的极简符号。
  2. mindmap.svg —— 思维导图,把"Pwn2Own 原链 → ratchet 修复 → 三层 IDA 反编译观察 → 顺序绕过 → 修复建议"五段串成一棵树。
  3. concept-cards.md —— 12 张关键概念卡片:UDS、SWCAN、A/B slot、partition gen_level、PSM、ratchet、Pwn2Own Automotive、0xFF00/0x201/0x202 等。
  4. glossary.md —— 28 条英中术语表,包含汽车诊断协议、固件结构、嵌入式安全常见缩写。

11. 谁应该读这篇文章

  • 嵌入式 / IoT / OTA 系统开发者:你正在设计的"防降级"机制可能在哪条路径上漏了?请把这篇文章当成检查清单。
  • 汽车 / 车联网安全研究者:本文是 Pwn2Own Automotive 案例库的一手材料。
  • 逆向工程师:David Berard 展示了一种"反编译比对 + 状态机推演"的纯净工作流。
  • AI 安全工具的开发者与用户:本文与 Calif/Mythos M5 那篇形成完美对照——理解 AI 能做什么,更要理解 AI 还不能做什么。
  • 产品经理 / 安全负责人:当你被告知"补丁已部署"时,你能问出"它覆盖了所有可达路径吗"这个问题吗?

📂 下载与延伸

🪶 编辑说明:原文为英文,本文为深度导读,所有原文引用均不超过 3 句、总引用 < 10%,结构与论证均为编辑原创。如发现事实错误,欢迎邮件指出。