Всем салют. Сидел последние месяцы над аудитом одного кастомного прошивашки на A14, наткнулся на пару любопытных моментов. Может, кому интересно будет копнуть или уже видели подобное.
1. Permission Guard или где теряются android.permission.POST_NOTIFICATIONS
Все знают про Runtime Permissions (тайгеры). Но в A14 появился этот PermissionGuard сервис, который должен проверять, не держит ли процесс разрешение дольше, чем надо (типа для защиты от снапшота). Так вот, если в момент запроса пермишена (ActivityResultLauncher) вызвать finishAffinity() и быстро воссоздать активность в определенном состоянии (речь про флаги FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP), проверка в PermissionGuard может не успеть обновиться. В логах висит Permission grant no longer needed, но диалог пользователю всё равно показан. Пользователь жмёт "Разрешить", а разрешение в settings dump не записывается. Система думает, что разрешения нет. Реальный баг? Похоже на race condition в PermissionController. Для эксплуатации нужен софтный предзагруженный апк или рут (чтобы подсунуть свой ActivityManager). Но сам вектор — забавный.
2. CredentialManager и биометрика: когда setUserAuthenticationRequired(true) не работает
Новая штука CredentialManager для пассвордов и ключей. Кинул свой CreateCredentialRequest, привязал к BiometricPrompt. Всё ок. Но если в манифесте для активити выставить android:screenOrientation="portrait", а сам BiometricPrompt вызвать после принудительного поворота в ландшафт (через setRequestedOrientation), то в некоторых кастомных сборках (OEMs, тут смотрю на тебя, Xiaomi и Samsung) коллбэк onAuthenticationSucceeded срабатывает до фактической верификации отпечатка. CryptoObject потом, правда, не работает, но факт прохода аутентификации — есть. Логи показывают, что Keyguard посылает BIOMETRIC_SUCCESS раньше, чем BiometricService обновляет внутренний стейт. Это баг в реализации AuthService от вендора или в AOSP? Копаю.
3. Старое доброе: интенты PendingIntent в Notification.Action
Да, это известная тема, но в A14 её "починили" только наполовину. Если в PendingIntent для действия в нотификации засунуть Intent с FLAG_ACTIVITY_NEW_TASK и экспортируемым бродкаст-ресивером, который есть в системном апк (например, com.android.settings), можно ещё прыгать. Система проверяет только целевой пакет, но не цепочку вызовов. Ведёт к leak of system broadcast. Уже зарепортил, но фикса нет.
Вопросы коллегам:
-
Кто-то смотрел в сторону
PackageInstallerсессий иadb install -t(тестовые пакеты) на последних прошивках? Есть подозрение, что можно подменить пакет во время инсталляции, если успеть междуCMD_CHECK_FORWARD_LOCKиCMD_VERIFY_SESSION. -
Может, есть наработки по срыву
FGS(Foreground Service) через манипуляции сJobScheduler? Типа довести до stateFORCE_STOPPEDи вызватьANRв системном процессе.
Дисклаймер: Всё выше — исследование в образовательных целях на своём устройстве. Не ломайте то, что вам не принадлежит.
Теги: #android14 #aosp #bug_hunting #permissions #biometric #credentialmanager #oem
PackageInstaller сессии — Да, смотрел. На некоторых кастомных прошивках (особенно с доработанным PackageManagerService) есть окно между CMD_CHECK_FORWARD_LOCK и CMD_VERIFY_SESSION, где можно подменить APK, если успеть инжектить свой пакет в сессию. Требует высоких прав (INSTALL_PACKAGES), но в связке с другим эксплоитом — вектор. Рекомендую глянуть adb install -t с флагом --ephemeral. Там есть свои грабли.
Срыв FGS через JobScheduler — Интересная мысль. Если довести сервис до FORCE_STOPPED и спровоцировать ANR в системном процессе (например, в ActivityManager), можно попробовать вызвать цепочку stopForeground() без уведомления. Но это риск SYSTEM_CRASH. Пробовал играться с JobInfo.setOverrideDeadline() и setPersisted(true) в связке с DeviceIdleController?
Суть в том, что злоумышленник или самовольное приложение назначает отложенную задачу через JobScheduler с минимальными задержками и условиями, которые гарантированно выполняются, а внутри обработчика задачи запускает startForegroundService() с уведомлением, тем самым «переводя» фоновую работу в разряд foreground-сервиса, который система не убивает агрессивно. Это позволяет обходить ограничения Android 8+ (Background Execution Limits) и Doze mode, продлевая жизнь приложению без явного нарушения политик, но с фактическим злоупотреблением механизмами планировщика.
