Сегодня в выпуске: обзор систем безопасности Android, объяснение атак типа Cloak & Dagger, гайд по извлечению данных с зашифрованной карты памяти, обход ограничений на загрузку нативных библиотек и доступ к скрытым Java-методам, уязвимость в Android Download Provider. А также: несколько инструментов пентестера и подборка библиотек для программистов.
The Android Platform Security Model — написанный сотрудниками Google вайтпейпер, посвященный теории и практике реализации подсистем безопасности в Android. В документе много воды, но есть и хоть и не новая, но полезная новичкам информация. Наиболее интересные моменты:
/data/data/имя_пакета
), но не может получить доступ к файлам других приложений и многим системным файлам. Система также использует UID (идентификатор пользователя) для контроля полномочий приложения./sdcard/Android
. Приложения могут хранить данные внутри нее без опасения, что другие приложения получат к ним доступ. Также Android позволяет подключить карту памяти в режиме Adoptable Storage, когда SD-карта форматируется в зашифрованную ФС ext4 и становится частью внутреннего хранилища данных.Cloak and Dagger — Mobile Malware Techniques Demystified — небольшая заметка о том, как работают атаки класса Cloak & Dagger. Мы писали об этом типе атак еще в 2017 году, но тогда рассмотрели только одну из них: кейлоггер, не требующий дополнительных прав в системе. Эта статья посвящена другой атаке, позволяющей заставить пользователя включить настройку доступа к AccessibilityService (позволяет перехватывать любые нажатия пользователя и нажимать кнопки интерфейса за него), замаскировав переключатель под нечто безобидное.
Атака использует разрешение SYSTEM_ALERT_WINDOW, которое приложения из Google Play получают автоматически. SYSTEM_ALERT_WINDOW позволяет выводить элементы интерфейса поверх других приложений, то есть реализовать такие вещи, как плавающие окна, меню, панели управления. Создатели вирусов быстро смекнули, что эту возможность можно использовать для перекрытия текущего окна на экране и обмана пользователя, поэтому с версией Android 5 Google выкатила защиту, которая проверяет, не был ли перекрыт какой-либо опасный для включения элемент интерфейса оверлеем, и отказывается его включить, если это так. Поэтому Cloak & Dagger вместо одного оверлея на весь экран создает несколько небольших и выкладывает их вокруг элемента управления, так что в результате защита не срабатывает.
Атака работает на Android версий 4.4.4–7.1.2, исходный код доступен.
В дополнение можно отметить еще одну статью на смежную тему: Android Toast Overlay Attack: “Cloak and Dagger” with No Permissions. Ее авторы пошли еще дальше и реализовали ту же атаку вообще без использования разрешения SYSTEM_ALERT_WINDOW. Вместо него они засунули все оверлеи в toast-сообщение, то самое, которое позволяет выводить в нижней части экрана информационные сообщения. Как оказалось, такие сообщения тоже представляют собой полноценные полноэкранные окна, большая часть которых прозрачна. И у приложения есть доступ к этому окну и возможность его изменять.
Recovering data from a failing Android adoptable storage — статья о том, как восстановить данные с карты памяти, отформатированной с помощью механизма Adoptable Storage. В отличие от обычного подключения SD-карты, Adoptable Storage создает на карте памяти зашифрованный том, форматирует его в файловую систему ext4, а затем подключает ее к основному хранилищу данных так, что на нее можно сохранять не только фотки с пляжа, но и приложения, их данные и любую другую информацию, которая обычно хранится только во внутренней памяти устройства. Другими словами, Adoptable Storage позволяет расширить встроенную память устройства.
Но есть одна, а точнее две смежные проблемы: 1) если вставить карту памяти в другой телефон — он ее не увидит из-за отсутствия ключа для расшифровки данных; 2) если что-то пойдет не так (например, карта памяти начнет сбоить), восстановить данные с нее не получится, точнее получится, но через одно место. Как через это место восстанавливать данные, описано в статье.
Для начала на телефоне необходимо получить права root. Затем подключить карту памяти к Linux-машине (macOS тоже должна подойти, но действия описаны именно для Linux) и снять ее образ. Обычный dd в этом случае не подойдет, так как, если карта памяти начала сбоить, он, скорее всего, вывалится с ошибкой Input/output error. Выручит ddrescue, который предназначен как раз для таких случаев:
$ sudo ddrescue -f -n /dev/mmcblk0 herolte_sd_recovery_fulldisk.img herolte_sd_recovery_fulldisk.log
Далее необходимо извлечь из памяти устройства ключ шифрования (на устройствах с активным модулем TEE такой трюк, скорее всего, не пройдет):
$ adb root > adb pull /data/misc/vold /data/misc/vold/: 1 file pulled. 0.0 MB/s (16 bytes in 0.013s) > ls vold bench expand_ffffffffffffffffffffffffffffffff.key > hexdump -e '16/1 "%02x" "n"' vold/expand_ffffffffffffffffffffffffffffffff.key aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%
Используем полученный ключ, чтобы смонтировать файловую систему:
$ sudo losetup --show --find -P herolte_sd_recovery_fulldisk.img $ sudo dmsetup create crypt1 --table "0 `blockdev --getsize /dev/loop0p2` crypt aes-cbc-essiv:sha256 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0 /dev/loop0p2 0" $ mkdir mnt $ sudo mount /dev/mapper/crypt1 mnt $ ls mnt app local lost+found media misc user user_de
Это все. Далее автор рассказывает, как залить файлы на другую карту памяти и изменить размер файловой системы. Про это можно прочитать в оригинальной статье. Также стоит отметить, что если карты памяти одинакового объема, то можно вообще не заморачиваться с подключением файловой системы на компе и копированием ключа, а просто залить полученный на первом шаге образ на новую карту памяти с помощью того же ddrescue:
$ sudo ddrescue -f -n herolte_sd_recovery_fulldisk.img /dev/mmcblk0 herolte_sd_recovery_fulldisk.log
Android Runtime Restrictions Bypass — статья о том, как обойти ограничения на доступ к внутренним библиотекам и методам Android.
Начиная с Android 7 Google ввела ограничения на прямую загрузку нативных системных библиотек (например, /system/lib/libart.so
). Позже, уже в релизе Android 9, появилось ограничение на доступ к определенным скрытым методам, которые раньше можно было вызывать с помощью рефлексии. Как оказалось, эти механизмы достаточно просто обойти.
Принцип работы механизма, ограничивающего загрузку библиотек, построен на пространствах имен. Если JNI-библиотека приложения попытается загрузить библиотеку, которая расположена за пределами каталогов /data
, /mnt/expand
или в песочнице самого приложения, оно будет завершено с такой ошибкой:
library "/system/lib64/libart.so" ("/system/lib64/libart.so") needed or dlopened by /data/app/re.android.restrictions-yALrH==/lib/arm64/librestrictions-bypass.so is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="/data/app/re.android.restrictions/..., permitted_paths="/data:/mnt/expand:/data/data/re.android.restrictions"]
Проверка осуществляется в лоадере библиотек (/system/bin/linker
), который создает для каждой JNI-библиотеки приложения структуру soinfo, которая хранит информацию о ней и пространствах имен, к которым она может получить доступ:
struct soinfo { ... android_namespace_t* primary_namespace_; android_namespace_list_t secondary_namespaces_; ... };
Все структуры soinfo размещены в мэпе g_soinfo_handles_map.
Интересно здесь то, что g_soinfo_handles_map — это экспортированная статическая переменная. Поэтому с помощью символьной таблицы ELF можно найти базовый адрес /system/bin/linker
, рассчитать адрес g_soinfo_handles_map JNI-библиотеки и изменить структуру soinfo, добавив нужные пути в доступное ей пространство имен:
android_namespace_t* ns = get_primary_namespace(soinfo_ptr); ns->set_ld_library_paths({"/system/lib64", "/sytem/lib"}); ns->set_isolated(false);
После этого попытка загрузить библиотеку будет успешной:
void Java_re_android_restrictions_MainActivity_openRestrict(...) { void* art_handle = dlopen("/system/lib64/libart.so", RTLD_NOW); }
Ограничение доступа к внутренним методам Java API, предназначенным только для использования системными компонентами, реализовано иначе, а именно с помощью прямых проверок на доступ. Например, функция GetStaticMethodID, используемая для доступа к Java-методам из JNI-библиотеки, вызывает функцию FindMethodID, которая в том числе проверяет, доступен ли данный метод:
static jmethodID FindMethodID(...) { ... if (... && ShouldBlockAccessToMember(method, soa.Self())) { ... } }
ShouldBlockAccessToMember() в конечном итоге вызывает метод Runtime::GetHiddenApiEnforcement(), который сообщает, стоит ли отклонить вызов или нет. При этом система может либо пропустить его без вопросов, либо вывести предупреждение, либо использовать черный и серый списки, которые содержат имена запрещенных к использованию методов.
Чтобы отключить проверку, мы должны перевести рантайм в режим «пропускать без вопросов» (EnforcementPolicy::kNoChecks), но для этого нам нужен доступ к самому рантайму:
#include <runtime/runtime.h> art::Runtime* current = art::Runtime::Current();
Однако в этом случае компилятор (а точнее, линковщик) будет вынужден импортировать символ art::Runtime::instance_ в JNI-библиотеку, то есть слинковать ее с libart.so. И здесь мы столкнемся с ограничением пространства имен, а предложенный ранее метод его обхода не сработает, так как мы не сможем изменить пространство имен раньше, чем в память загрузится libart.so.
Но есть другой способ получить доступ к рантайму. Дело в том, что метод JNI_OnLoad, который запускается при загрузке JNI-библиотеки, в качестве первого аргумента получает указатель на art::JavaVMExt, который имеет метод GetRuntime(). Так что все, что нам остается, — это получить доступ к рантайму и отключить проверку:
static art::Runtime* my_runtime = nullptr jint JNI_OnLoad(JavaVM *vm, void *reserved) { my_runtime = reinterpret_cast<art::JavaVMExt*>(vm)->GetRuntime(); my_runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kNoChecks); return JNI_VERSION_1_4; }
Примечательно, что команда Android security team не считает описанные методы обхода нарушением безопасности (мол, не для безопасности они были придуманы), поэтому быстро дала согласие на обнародование информации и публикацию исходного кода PoC.
Multiple Vulnerabilities in Android’s Download Provider — статья исследователя, нашедшего три уязвимости в Android: CVE-2018-9468, CVE-2018-9493 и CVE-2018-9546. Все они затрагивают Download Content Provider, компонент, позволяющий любому приложению запустить загрузку файла из интернета так, чтобы пользователь видел уведомление с прогрессом загрузки.
content://downloads/public_downloads/#
, зловредное приложение может подобрать загрузку по номеру и, например, прочитать загруженный файл или подменить его сразу после загрузки. Причем это относится как к обновлениям ПО, так и к загрузкам из Google Play. Исходный код примера.Данные уязвимости были исправлены в сентябрьском и ноябрьском security-патчах.
Читайте также
Последние новости