
Android verified boot 이미지(vbmeta)가 공개적으로 알려진 테스트 키로 서명되었는지 확인하여 릴리스 빌드의 잘못된 구성을 식별하는 Python 유틸리티입니다.
NCC Group 컨설턴트들이 가끔 마주치는 오류는 알려진 개인 키(known private key)로 Android 빌드를 서명하는 것입니다. 이는 보안 부팅(secure boot)이 활성화된 기기에서조차 제조사(그리고 결과적으로 사용자)를 취약하게 만듭니다. 이 블로그 포스트의 목표는 두 가지입니다:
Android 기반 기기가 부팅되면, 먼저 부트로더가 서명된 코드를 실행하는지 검증되고, 그 다음 부트로더가 상위 운영체제(HLOS)를 검증합니다. 이 블로그 포스트는 후자만 다룹니다.
각 칩셋 벤더는 신뢰 루트(RoT)를 다르게 구현할 수 있지만, 일반적으로 기기는 *e-퓨즈*를 구성하여 보안을 강화합니다. 칩셋에 내장된 불변 ROM 코드는 이 e-퓨즈를 읽고 이를 암호화 해시의 비트로 해석합니다. 그런 다음 해시는 일반적으로 부트로더 바이너리에 포함된 신뢰할 수 있는 공개 키를 검증하는 데 사용됩니다. 공개 키가 검증되면, 이후 기기에서 실행되는 첫 번째 수정 가능한 코드가 서명되었는지 암호화 방식으로 검증하는 데 사용됩니다. 이 프로세스가 올바르게 구현되면 부트로더를 수정하려는 모든 시도는 실패합니다. Android 기반 기기(보안 카메라, 스마트 플러그, 센서 등)를 제조하는 OEM은 일반적으로 칩 벤더(예: Qualcomm)의 레퍼런스 디자인과 샘플 코드를 제품의 출발점으로 사용합니다.
*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 빌드가 올바르게 서명되었는지 확인하거나, 그렇지 않으면 빌드가 실패하도록 할 수도 있습니다.