
PoC для CVE-2021-39749, позволяющий запускать произвольное Activity на Android 12L Beta
Это PoC для CVE-2021-39749, который позволяет запускать активности других приложений на Android 12L Beta независимо от их настроек permission и exported
В Android 12L доступ к TaskFragmentOrganizer (намеренно) больше не требует разрешения MANAGE_ACTIVITY_TASKS
Использование приложения, представленного здесь, требует отключения проверок скрытых API, это можно сделать через adb shell settings put global hidden_api_policy 1. Это не является границей безопасности, и существуют известные обходные пути на уровне приложений
Вот коммиты, исправляющие эту ошибку (и несколько связанных, упомянутых в оригинальном отчёте):
startActivityInTaskFragment больше не полагается на Binder.getCallingUid()ResolverActivity теперь имеет включённый relinquishTaskIdentitySurfaceControl для TaskFragment больше не предоставляетсяActivityRecord#appToken в TaskFragmentOrganizer, теперь основано на uid, а не на pidВы можете переключиться на android-12.1.0_r4, откатить первые 3 коммита (или первые 2, приложение всё равно сможет выключить устройство (запустив ShutdownActivity), но флажок "Zoom and set alpha" не будет работать)
(Первый коммит из этого списка будет иметь конфликты слияния в тестах, если вы попытаетесь его откатить, но их можно игнорировать)
Binder.getCallingUid(), который всегда возвращает системный uidМетод Binder.getCallingUid() возвращает uid процесса, отправившего текущую обрабатываемую Binder-транзакцию. Этот uid хранится в переменной, локальной для потока. Код, обрабатывающий транзакцию, может вызвать Binder.clearCallingIdentity(), чтобы установить эту переменную в uid собственного процесса, указывая методам, вызываемым позже во время обработки транзакции, что проверки разрешений должны выполняться против самого себя (кода, обрабатывающего транзакцию), а не вызывающего Binder-транзакцию
Иногда встречаются вызовы Binder.getCallingUid(), которые всегда вызываются после Binder.clearCallingIdentity(), и поэтому всегда возвращают uid собственного процесса. Иногда это происходит намеренно, например, в ActivityTaskManagerService#startDreamActivity (хотя это довольно запутанный способ сделать Process.myUid() или Os.getuid())
Я написал для себя (основанный на Soot) инструмент статического анализа, который сообщает о таких вызовах Binder.getCallingUid() (и других проверках разрешений), которые могут произойти только после Binder.clearCallingIdentity(). (У меня есть собственная логика обработки Jimple/Shimple IR, предоставляемого Soot, хотя, возможно, есть лучший способ сделать это с помощью Soot, но это то, что у меня есть сейчас)
В Android 12L Beta этот инструмент нашёл один такой вызов в ActivityStartController#startActivityInTaskFragment (Примечание: исходный код тогда не был доступен, так как Beta-релизы не являются открытым исходным кодом, но Shimple в целом читаем, поэтому я использовал Soot также как декомпилятор Java)
startActivityInTaskFragmentВ рамках отчёта статического анализа я получил иерархию вызовов от реализации onTransact() (где начинается Binder-вызов) до startActivityInTaskFragment:
onTransact в сгенерированном aidl-коде IWindowOrganizerControllerWindowOrganizerController#applyTransaction (без аргумента CallerInfo)WindowOrganizerController#applyTransaction (с аргументом CallerInfo)WindowOrganizerController#applyHierarchyOpActivityStartController#startActivityInTaskFragmentЯ обнаружил, что Binder-вызовы applyTransaction присутствуют в классе TaskFragmentOrganizer, и решил использовать его как более удобную обёртку, чем выполнять все Binder-вызовы напрямую (ни то, ни другое не является публичным API, поэтому в любом случае пришлось использовать рефлексию)
Прежде всего, метод "2." вызывает enforceTaskPermission, который на Android 12.0 проверял разрешение MANAGE_ACTIVITY_TASKS только для signature, которое мы не могли получить, однако на Android 12L правила были смягчены, так что определённые транзакции можно выполнять без разрешений. Оказалось, что ни одна из операций, необходимых для выполнения startActivityInTaskFragment, не требовала разрешения (если с транзакцией был связан TaskFragmentOrganizer)
Итак, мы хотим выполнить HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT. Для этого наш TaskFragment должен быть зарегистрирован в mLaunchTaskFragments, иначе будет сообщено об исключении "Not allowed to operate with invalid fragment token"