NCC Group 的顾问偶尔会遇到这样的问题:Android 构建使用了已知的私钥进行签名。这使设备制造商(以及他们的用户)暴露在风险之中,即使在启用了安全启动(secure boot)的设备上也是如此。这篇博文有两个目标:
当基于 Android 的设备启动时,首先会验证引导加载程序(bootloader)运行的是已签名的代码,随后引导加载程序验证高级操作系统(HLOS)。这篇博文仅涉及后者。
每个芯片组供应商都可以以不同方式实现信任根(RoT),但通常设备通过配置*e-fuses* 来确保安全。烧录在芯片组上的不可变 ROM 代码读取这些 e-fuses,并将其解释为加密哈希的比特位。该哈希随后用于验证受信任的公钥,该公钥通常包含在引导加载程序二进制文件中。公钥通过验证后,便用于加密验证*设备上运行的第一个可修改代码是否已签名*。如果此过程实现正确,任何修改引导加载程序的尝试都将失败。制造基于 Android 的设备(安防摄像头、智能插头或传感器等)的 OEM 通常以芯片供应商(即高通)的参考设计和示例代码作为其产品的起点。
AVB 是确保设备上运行的软件完整性的机制的一部分。设备包含一个名为 vbmeta 的分区,其中包含一个经过加密验证的映像。引导加载程序成功完成验证后,设备便会信任 vbmeta 映像的内容。该映像包含引导加载程序随后用于验证 boot、vendor 或 system 等分区上软件的信息。
为方便起见,芯片组供应商提供开箱即可成功编译的参考代码,以简化 OEM 的设备启动(bring-up)流程。最初,这些映像使用*测试私钥* 签名,这些测试私钥最初由 Google 生成,并存在于所有原生 Android 构建中。有时芯片供应商会更新这些测试密钥,但私有签名密钥仍然存在于示例代码中,因此必须被视为不可信。
NCC Group 发现,设备制造商可能会通过配置熔丝来正确保护引导加载程序,但他们不会更改用于签名 HLOS 的默认测试私钥。因此,虽然引导加载程序无法被修改,但攻击者可以构建并签名自定义 HLOS 代码,并更新将通过 AVB 过程成功验证的分区。
NCC Group 创建了一个*工具*,用于检查 vbmeta.img 是否包含已知私钥的公开部分。对于测试密钥,该工具接受用户提供的路径(由 Android 构建中的 BOARD_AVB_KEY_PATH 环境变量指定),或者使用随工具附带的一组从 GitHub 收集的已知密钥。
$ python3 test_avb_key.py --help
Usage:
python test_avb_key.py [VBMETA.IMG] [PATH_PRIVATE_KEY]
Parameters:
* Parameter 'VBMETA.IMG' points to a user or userdebug file from an Android build.
Note: the userdebug image fails this test, it is expected.
* Grep Android build for 'BOARD_AVB_KEY_PATH' to obtain path of signing file,
and leave only the path, i.e. 'external/avb/test/data/'.
如果 HLOS 软件已使用默认私钥签名,则会标记该问题。例如,对于 LineageOS 构建:
$ python test_avb_key.py ./sample/vbmeta/lineage/vbmeta.img ./sample/aosp/external/avb/test/data/
Opening vbmeta file: ./sample/vbmeta/lineage/vbmeta.img
Using known private key for verification: ./sample/aosp/external/avb/test/data/sign_key.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_atx_pik.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_atx_prk.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_atx_psk.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_atx_puk.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_rsa2048.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_rsa2048_gsi.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_rsa2048_oneplus.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_rsa4096.pem. Public key found at index: 945
If the script was executed on a vbmeta.img file from an Android user build, there is a problem.
否则,该工具会返回成功消息,例如 Pixel9 构建:
$ python test_avb_key.py ./sample/vbmeta/pixel9/vbmeta.img ./sample/aosp/external/avb/test/data/
Opening vbmeta file: ./sample/vbmeta/pixel9/vbmeta.img
Using known private key for verification: ./sample/aosp/external/avb/test/data/sign_key.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_atx_pik.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_atx_prk.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_atx_psk.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_atx_puk.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_rsa2048.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_rsa2048_gsi.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_rsa2048_oneplus.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_rsa4096.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_rsa4096_oneplus.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_rsa4096_realtek.pem. Public key not found in vbmeta.img file. That's good.
Using known private key for verification: ./sample/aosp/external/avb/test/data/testkey_rsa8192.pem. Public key not found in vbmeta.img file. That's good.
No issues were found with ./sample/vbmeta/pixel9/vbmeta.img
Android 开发者可以使用此工具作为快速检查,以确保发布/生产构建未使用公开已知的私钥签名。该工具也可以集成到构建流程中,以确保 Android user 构建签名正确,否则构建将失败。