
Utilidad de Python para comprobar si las imágenes de arranque verificado (verified boot) de Android (vbmeta) están firmadas con claves de prueba públicas conocidas, identificando malas configuraciones en compilaciones de lanzamiento.
Un error que a veces encuentran los consultores de NCC Group es firmar builds de Android con claves privadas conocidas. Esto deja a los fabricantes de dispositivos (y, por lo tanto, a sus usuarios) vulnerables, incluso en dispositivos donde el arranque seguro (secure boot) está habilitado. Esta entrada de blog tiene dos objetivos:
Cuando los dispositivos basados en Android arrancan, primero se verifica que el bootloader esté ejecutando código firmado; después, el bootloader verifica el sistema operativo de alto nivel (HLOS). Esta entrada de blog solo cubre esta última parte.
Cada fabricante de chipset puede implementar la raíz de confianza (RoT) de forma diferente, pero normalmente los dispositivos se aseguran configurando e-fuses. El código ROM inmutable que está grabado en el chipset lee estos e-fuses y los interpreta como bits de un hash criptográfico. El hash se usa entonces para verificar una clave pública de confianza, normalmente incluida en el binario del bootloader. Una vez validada la clave pública, se utiliza posteriormente para verificar criptográficamente que el primer código modificable que se ejecuta en el dispositivo está firmado. Si este proceso se implementa correctamente, cualquier intento de modificar el bootloader fracasará. Los OEM que fabrican dispositivos basados en Android (cámaras de seguridad, enchufes inteligentes o sensores, etc.) suelen usar el diseño de referencia y el código de ejemplo del fabricante del chip (p. ej., Qualcomm) como punto de partida para su producto.
AVB forma parte del mecanismo utilizado para garantizar la integridad del software que se ejecuta en un dispositivo. Los dispositivos contienen una partición llamada vbmeta que contiene una imagen verificada criptográficamente. Después de que el bootloader realiza una verificación exitosa, el dispositivo confía en el contenido de la imagen vbmeta. La imagen contiene información que el bootloader utiliza posteriormente para validar el software presente en particiones como boot, vendor o system.
Por conveniencia, los fabricantes de chipsets proporcionan código de referencia que compila correctamente sin configuración adicional, para simplificar el proceso de puesta en marcha (bring-up) de dispositivos del OEM. Inicialmente, estas imágenes se firman usando claves privadas de prueba, generadas originalmente por Google y presentes en todos los builds de Android vanilla. A veces los fabricantes de chipsets actualizan estas claves de prueba, pero las claves privadas de firma siguen presentes en el código de ejemplo, por lo que deben considerarse no confiables.
NCC Group descubrió que los fabricantes de dispositivos pueden asegurar el bootloader correctamente configurando los fusibles; sin embargo, no cambian la clave privada de prueba predeterminada utilizada para firmar el HLOS. Por lo tanto, aunque el bootloader no se puede modificar, un atacante podría compilar y firmar código HLOS personalizado y actualizar particiones que serán verificadas correctamente por el proceso AVB.
NCC Group creó una herramienta que comprueba si vbmeta.img incluye la parte pública de una clave privada conocida. Para las claves de prueba, la herramienta acepta una ruta proporcionada por el usuario (indicada por la variable de entorno BOARD_AVB_KEY_PATH en el build de Android), o utiliza una colección de claves conocidas recopiladas de GitHub que se incluyen con la herramienta.
$ 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/'.
Si el software HLOS ha sido firmado con una clave privada predeterminada, el problema se señala. Por ejemplo, para el build de 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.
De lo contrario, la herramienta devuelve un mensaje de éxito; por ejemplo, en el build de 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
Los desarrolladores de Android pueden usar esta herramienta como comprobación rápida para asegurarse de que los builds de lanzamiento/producción no estén firmados con claves privadas conocidas públicamente. También podría integrarse en el proceso de compilación para garantizar que los builds de usuario de Android estén firmados correctamente; de lo contrario, la compilación fallará.