Уязвимости Android приложений. Часть 2

Уязвимости Android приложений. Часть 2

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

Уязвимости компонентов Android

Intent

Я напомню, что Intent — это инструмент запроса действия от одного компонента другим. Например, вызов второго Activity по нажатию кнопки(т.е компонент Button запрашивает через Intent действие в виде переключения Activity). Intent связывает разные компоненты и позволяет им общаться между собой (компонентом может быть что угодно: ImageView, Button, Activity, WebView и т.д)
Проблема Intent заключается в том, что стороннее приложение может перехватить и подменить этот самый Intent для совершения вредоносных действий.

Intent делятся на два типа:

  1. Явные

С ними все более или менее понятно, берется один компонент, и напрямую связывается с другим(с фильтром или без)

Пример кода:
Вызов Activity:
Intent intent = new Intent(this, SecondActivity.class);
StartActivity(intent)

  1. Неявные

Неявные Intent представляют собой передачу данных между компонентом А, и явно не заданным компонентом В. То бишь Intent компонента А может обработать любое приложение, используя Intent Filter. Самый понятный пример, это когда вы открываете какую-то ссылку, и система Android предлагает вам открыть ее браузером на выбор во всплывающем нижнем окне. Кстати, здесь хорошо видна суть Intent фильтров.
Может получиться так, что у вас установлено 5 браузеров, но открыть ссылку может только 4 браузера. Именно это и означает, что у одного из браузеров нет фильтров для открытия этой ссылки. 
Т.е ваше приложение ищет соответствие путем сканирования всех Manifest файлов всех приложений, и если ваш Intent фильтр на открытие Activity в другом приложении будет совпадать по значениям с Intent фильтром другого Activity в другом приложении, то это Activity и будет вызвано

Пример кода:
Intent intent = new Intent();
intent.addAction("CustomAction");
intent.addCategory("CustomCategory");
startActivity(intent);

Файл манифеста:
<activity android:name="SecondActivity">
<intent-filter>
<action android:name="CustomAction"/>
<category android:name="CustomCategory"/>
</intent-filter>
</activity>

Тут же стоит сделать небольшое пояснение насчет строк:
intent.addAction("CustomAction");
intent.addCategory("CustomCategory");

Action — это то действие, которое Intent должен совершить(открыть ссылку, открыть Activity или передать данные)

Так же для дальнейшего понимания:

Data — это данные в формате URI, которыми будет оперировать Intent. Как выглядит блок данных, будет показано позже.
Category - это строка, которая содержит информацию о типе компонента, который обрабатывает Intent.
Extras – пара «ключ-значение», дающая Intentу дополнительную информацию.

Если сразу непонятны некоторые моменты, не волнуйтесь, все это мы еще несколько раз рассмотрим на практике, но самое важное из всего выше, что в общих чертах Intent выглядит как:
Intent = Data + Action + Category

После всего этого возникает главный вопрос:

Что если вредоносное приложение будет использовать точно такой же Intent фильтр с такими же параметрами, как в целевом приложении?

Ответ прост: подмена и перехват данных

Давайте для наглядности создадим два приложения. Первое будет создавать неявный Intent со своим Intent фильтром, а второе приложение этот Intent будет перехватывать.

Первое приложение:

Приложение с кнопкой

После нажатия кнопки нас перекидывает на второе активити(intentTest) с таким текстом:

Текст 2 активити

Код в MainActivity, которое неявным Intent’ом через action вызывает IntentTest activity

Так же при помощи Extra мы передаем чувствительные данные вместе с Intentом:

Передача чувствительных данных

Содержимое файла Manifest, где четко видно, что поиск нужного action прописан при помощи строчки в intent-filter:

Файл Manifest

Примечание: если обратить внимание, то android:name в IntentTest был записан в иной форме(в виде пакета приложения), которая пригодится нам далее

Второе приложение:

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

Код EvilAct
Тут идет самая банальная запись данных с Extra внутри Intent на экран телефона:

Банальная запись данных с Extra внутри Intent

Код в MainActivity:
Код в MainActivity

Файл манифеста:
Файл Manifest

И теперь когда я жму на кнопку в первом приложении, выходит вот такой экран из-за сходства фильтров:

Показ сходства фильтров

Конечно же откроем мы нашим EvilApk:
Открытие через EvilApk

Тут нас и встречают наши конфиденциальные данные, которые передавались по неявному Intent.

Но возникает еще один резонный вопрос. Видно ли в том же Jadx фильтры Intent?
Ответ: отчетливо.
Jadx фильтры Intent

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

WebView

Для начала давайте разберемся, что из себя в целом представляет WebView.

Наверняка вы часто сталкивались с открытием внутреннего браузера для просмотра веб страничек в приложении. Примером может служить Telegram, который при открытии любой ссылки открывает WebView(встроенный браузер), а не Chrome или Firefox.

Для дальнейшего разбора уязвимостей этого компонента, давайте сначала рассмотрим его работу на примере простого для понимания кода:

Пример работа уязвимого компонента

Код достаточно прост и не требует пояснений кроме одной важной строчки, где мы включили поддержку js. После такого шага у подкованных в веб уязвимостях читателей возникают интересные мысли насчет xss атак, которые, конечно же, будут.
XSS-атака

Вообще, помимо прямых ссылок из интернета в WebView можно также передавать свои страницы, хранимые в памяти устройства. Во время разработки они должны располагаться в папке assets. К ним доступ будет происходить через URI конструкцию, например так: file://android_res/custom_page.html. 
Понятное дело, что под словом веб страничка может быть вообще что угодно: картинка, простенькая форма или сложный динамичный сайт с js под капотом.

В чем же заключаются проблемы с WebView?

Вообще, главная проблема WebView - это сама связь с веб приложениями как таковыми, т.к помимо проблем с Intent(в контексте WebView еще поговорим ниже), есть и проблемы, присущие сайтам: веб уязвимости по типу XSS, отсутствие у загружаемого ресурса ssl соединения, mitm атаки и т.д. 

Давайте по порядку:

1) Неявные Intent

Вполне логично, что некоторые приложения получает ссылку на открытие внутри приложения через неявный Intent(это присуще приложениям, которые при открытии ссылки со своих крупных сайтов перекидывают вас внутрь приложения)
Тут и вытекают проблемы, на примерах выше мы смогли через одинаковый Intent фильтр перехватить данные, но что если их перезаписать?
Правильно, мы сможем вызвать банальную XSS атаку, если вредоносное приложение имеет тот же Intent фильтр и если мы сможем перезаписать данные ссылки 

2) Проблемы с внутренними файлами

Выше было сказано, что webview может рисовать локальные файлы. Но что если вместо локального файла мы сможем подать на вход какой-то другой, конфиденциальный путь?

Допустим, в коде есть такая строка:
webView.loadUrl(‘’file://android_res/profile.html’’ + name);

А теперь давайте подумаем, что если вместо name = John мы напишем:
name = «/../../../../../sdcard/Downloads/Photos/1.jpg»

Верно, наши строки сконкатенируются и мы получим изображение 1.jpg.
Но как это должно делать вредоносное приложение, если у него нет прямого доступа на изменение кода легитимной программы?
Тут на помощь приходят неявные Intent. В первой половине статьи мы перехватили конфиденциальные данные, которыми могут оказаться и те данные, которые впоследствии и будут отрисовываться при помощи WebView. Опять же, все зависит от конкретного приложения, но если есть данные, которые вредоносное приложение может явно контролировать, стоит обратить внимание на WebView и его параметры.
 

3) Deep Links

Давайте представим, что у нас есть специальное приложение для работы с нашим крупным веб-ресурсом(например, соц сеть) и мы делаем приложение, которое позволяет пользователю анализировать активность на своей странице

Опустим моменты с подробной реализацией и задумаемся глобально. Наверняка url адрес нашего профиля будет примерно такой:
https://my-social/profiles?user=codebyschool 

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

Т.е нам нужно решить две проблемы:

  1. Открытие подобного шаблона ссылки из любого приложения нашим приложением
  2. Оптимизировать это под каждого пользователя

Тут нам на помощь приходят deep links, которые андроид приложение будет разбивать на части, вытаскивать нужную информацию и манипулировать с данными
Логично, что для открытия подобных ссылок из других приложений нашей программой, мы должны написать неявный Intent, который будет запускаться, когда клик по ссылке будет осуществлен.
А теперь давайте подумаем. У нас есть приложение, которое открывает шаблонизированные ссылки, вытаскивая и обрабатывая оттуда какие-то данные.

Например, у нас есть приложение, которое работает со ссылками вида:
http://my-site.com/rename-user?user=John

И изменяет при работе с данными в параметре user введенное в приложении имя пользователя, после отображая новый профиль с новым именем в WebView. 
А теперь давайте подумаем, что если мы вытащили путем обратного инжиниринга этот url адрес, и теперь просто создадим веб страницу со ссылкой выше, по которой кликнет пользователь?

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

Broadcast receivers

Часто приложению нужно получать информацию от системы или от других приложений. Например, если у вас приложение, которое анализирует использование батареи, т.е вам постоянно нужно получать заряд батареи. Если же у вас приложение, которое при регистрации запрашивает код с смс, вам нужно каким-то образом получить уведомление о приходе смс на указанный номер.
Согласитесь, постоянно обращаться к андроиду и спрашивать у операционной системы заряд батареи - это достаточно странно.
Именно поэтому и придумали Broadcast receiver(широковещательный приемник)
Этот компонент постоянно «слушает» устройство и выдает вам то, что вы ждете от системы или других приложений.
Т.е у вашего приложения есть специальный приемник, который ловит запросы от системы или приложений. Или же у вас есть специальный формат широковещательных обращений, которые будут идти на вход к чужим приемникам.
 

Есть два вида broadcast receiver:

  1. Явный. В нем мы явно указываем названия приложений, которые будут получать наши широковещательные сообщения.
  2. Неявный. При таком объявлении уведомления будут получать все приложения. Чтобы принимать такие сообщения нужно прописать в Intent фильтр необходимые настройки.

Какие проблемы есть у приемников?

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

Notification Service

Наверняка вы часто сталкивались с push уведомлениями от различных приложений. Они и реализуются при помощи Notification Service.
Наше приложение по нажатию кнопки будет выводить уведомление, при клике на которое нас перебросит на неявно вызванное Activity с надписью success. Неявно вызванное Activity в связке с Notification может использоваться, если у нас будет два приложения, которые смогут открывать одинаковые уведомления. Например, неофициальные клиенты каких-либо мессенджеров или соц.сетей

Уведомление, при клике на которое нас перебросит на неявно вызванное Activity с надписью success

Надпись Success

Техническая реализация:

Техническая реализация

Техническая реализация

Если вчитаться в код, то никакого Notification сервиса здесь нет, но давайте подумаем, что если мы создадим второе приложение, которое будет иметь разрешение на прочтение всех уведомлений с устройства?
Да, такие приложения бывают, и даже легитимные приложения зачастую читают уведомления в смс, чтобы, например, автоматически вставить код, подтверждающий при регистрации номер телефона.
Делается такой перехват при помощи сервиса Notification Listener Service, и в интернете очень много его реализаций. Например, такой сервис, примеры кода которого очень легко найти, может подменять уведомления от того же WhatsApp, даже была ситуация, когда любое приложение, имеющее доступ к чтению всех уведомлений, могло перехватить текст сообщения, отправленного через Gmail, а как мы с вами знаем, некоторые сервисы могут отправлять пароли при регистрации на почту. 

Content provider

Представьте, что ваше приложение хранит какие-то данные(банально, базу данных) и вы хотите делиться ими, полностью не давая доступ к тем или иным данным в общем объеме, тут и приходит на помощь content provider, который помогает организовать передачу данных от одного приложения к другим.
В content provider все данные извлекаются и записываются при помощи специально настроенных URI строк(они похожи на строки, которые мы рассмотрели в пункте про WebView)
Например, мы хотим получить через Content Provider список пользователей в базе данных SQLite, строка примет такой вид:
content://com.android.contentapp.userprovider/users

Т.е по сути мы обращаемся к базе данных и извлекаем строки, но этот запрос, записанный в форме content provider будет записан в такой простой форме.

А теперь давайте представим, что наш провайдер может работать с абсолютно любым приложением на устройстве(как, например, системные приложения с контактами).
Если это так, то мы можем самостоятельно вписывать и извлекать значения из той же базы данных. Ничего не напоминает?

Правильно, небезопасная работа провайдера с абсолютно любым приложением может привести к Sql инъекции, которая может извлекать данные о пользователях, которые работали в приложении или иную информацию, которая была записана в таблице или файлах, к которым был прикреплен контент провайдер.

Зачастую разработчики хардкодят(пишут открытым текстом) URI схему контент провайдера. Вкупе с анализом файла манифеста, где может быть установлен легко читаемый Intent фильтр или exported: «true» значение, такой небезопасный код может привести к тому, что вредоносное приложение сможет записывать свои данные в таблицу другого приложения, просто имея нужные данные для провайдера. Более того, давно есть инструмент drozer, который всего одной строкой может извлечь или записать данные в выбранный контент провайдер 

Заключение

Андроид приложения, как и любые другие приложения под различные системы имеют уязвимости. И если стоит цель найти эти самые уязвимости, стоит досконально изучить код и работу архитектуры андроид. Андроид приложения в целом являются очень громоздкими, поэтому разработчик банально для быстроты написания кода может ставить exported:«true» и забывать закрывать доступ к компонентам приложения, даже не осознавая, к чему это может привести.