Привет, Хабр! Сегодня я хочу рассказать про безопасность мобильных приложений со стороны атакующего.
Мобильные приложения активно используют данные, а значит, нуждаются в грамотной защите. Поэтому за последние несколько лет значительно выросло количество проектов, связанных с анализом их защищенности. Заинтересованность компаний в безопасности мобильных приложений можно заметить и на рынке багбаунти. Например, на BI.ZONE Bug Bounty мобильные приложения есть в 19 публичных программах.
Давайте рассмотрим подробнее, какие уязвимости встречаются в мобильных приложениях
Этот текст написан по мотивам моего выступления на VolgaCTF.
Пример уязвимости
Дальше речь пойдет именно про Android, так как он более доступный для исследования безопасности.
Критичность client-side-уязвимостей меньше, чем server-side, так как они касаются конкретного пользователя, а не сервис. Но есть главная особенность, из-за которой уязвимости в мобильных клиентах могут быть лакомым кусочком для злоумышленников. В отличие от веб-приложений, они напрямую взаимодействуют с операционной системой устройства, а значит, имеют куда больше возможностей.
Мобильные приложения могут напрямую взаимодействовать:
с файловой системой девайса,
ОС через системные вызовы,
sh-оболочкой,
камерой, микрофоном, гироскопом и другими периферийными устройствами,
другими приложениями на девайсе.
Давайте поэкспериментируем и посмотрим, до чего сможем дотянуться на устройстве при помощи RCE. Для эмуляции наличия RCE на устройстве я использовал AndroRAT и эмулятор девайса от Android Studio.
Возможности Interpreter AndroRAT из коробки уже удивляют.
С RCE на устройстве мы получаем доступ к следующим данным:
-
Информация о девайсе.
-
Информация о IP- и MAC-адресе устройства.
Камера (из-за особенностей эмулятора не удалось к ней подключиться).
-
История сообщений, входящих и исходящих звонков.
-
Геопозиция устройства.
-
Информация о сим-карте и провайдере.
-
Микрофон.
-
Буфер обмена.
Часто приложения имеют доступ к SD-карте, где может храниться много интересного: от кеша приложений до фотографий пользователя.
Ничто не мешает злоумышленнику загружать свои утилиты и в дальнейшем использовать их для постэксплуатации и закрепления. Для демонстрации я скомпилировал и загрузил свою версию URL, с ее помощью получил доступ к ресурсу из внутренней сети.
Таким же образом злоумышленник может скомпилировать и загрузить на устройство:
утилиты для разведки и сканирования сети,
утилиты для проксирования трафика через устройство,
вредоносное ПО: майнеры, беконы для создания ботнета и т. п.
Далеко не каждое RCE даст столько возможностей. Все зависит от гибкого механизма настройки permission в Android или указания property в iOS. Благодаря этому можно указать, к чему приложение должно иметь доступ, а к чему нет.
Со строгой политикой можно свести импакт от RCE примерно к нулю, максимально ограничив доступ приложения вплоть до взаимодействия по сети. Но в этом случае приложение будет урезано в функциях, что не всегда удобно.
Например, приложение «Хабра» на Android запрашивает не так уж много разрешений.
При обнаружении теоретического RCE максимум, что мы сможем, — записывать и исполнять произвольные файлы в директорию приложения (вне зависимости от разрешений), а также взаимодействовать с сетью устройства. На этом все.
Но если посмотреть, сколько разрешений запрашивает условный Telegram, не хватит одного скриншота, чтобы уместить все.
И Telegram не исключение. По статистике от Cybernews легко заметить, что 3 из 4 приложений запрашивают доступ к внешнему хранилищу данных, а каждое третье — просит доступ к микрофону и камере. Приложения часто запрашивают права, которые им не нужны. Это связано с тем, что некоторые разработчики просят по умолчанию давать расширенные права для приложения. Так что исключением служит скорее приложение «Хабра» с относительно небольшим набором прав.
Импакт от уязвимостей действительно существует, но он зависит от вида уязвимости, а также от установленных для приложения разрешений. Но в среднем критичность в мобильных приложениях будет выше, чем у client-side-уязвимостей в веб-приложениях.
Классификация уязвимостей
Есть несколько мнений, как можно классифицировать уязвимости в мобильных приложениях. Рассмотрим такие.
По вектору эксплуатации
-
0-click. Для эксплуатации уязвимости от пользователя не требуется никаких действий, будь то любое взаимодействие или установка дополнительных приложений. Достаточно отправить пользователю сообщение с полезной нагрузкой, которая будет обработана приложением без дополнительного взаимодействия с человеком.
-
1-click. Для эксплуатации необходимо, чтобы пользователь совершил одно действие: перешел по ссылке, открыл вложение и т. п.
-
Malware app. Для эксплуатации необходимо, чтобы пользователь установил вредоносное приложение, которое использует уязвимость, связанную с межпроцессорным взаимодействием в другом приложении.
-
MITM. Для эксплуатации необходимо, чтобы злоумышленник реализовал атаку man-in-the-middle и мог контролировать сетевой трафик.
Физический доступ. Для эксплуатации уязвимости необходим физический доступ к устройству.
Первые три типа имеют реальный вектор для массовой эксплуатации, поэтому их можно считать наиболее критичными. За такие уязвимости в большинстве случаев можно получить баунти в багбаунти-программах, где есть мобильные приложения.
Последние два хоть и кажутся безобидными, все равно могут нести серьезные риски. Вряд ли кто-то захочет увидеть уведомление о списании средств со своего банковского счета после того, как ненадолго забыл телефон на кухне в офисе или подключился к общедоступному Wi-Fi.
Инъекции
Инъекция — это уязвимость, при которой злоумышленник вводит вредоносные данные в приложение, чтобы изменить его поведение, обойти защиту или выполнить нежелательные команды.
В отличие от веб-приложений, где в client-side можно внедрять CSS, HTML и JS, здесь список значительно шире.
В некоторых местах мобильного приложения используется WebView, который по сути является внутренним браузером, так что в нем мы можем обнаружить такие же client-side-уязвимости, что и в веб-приложении.
Чтобы допустить XSS, достаточно включить поддержку javascript в WebView. По опыту он включен в 99 случаев из 100. Когда в последний раз вы видели веб-страницу без JS?
webView.getSettings().setJavaScriptEnabled(true);
Недостаточно экранировать пользовательский ввод: тут все, как в веб-приложении.
Из интересных особенностей: в некоторых случаях разработчики могут реализовать javascript-интерфейс, который позволит вызвать java-код из javascript и тем самым получит возможность взаимодействовать с ОС.
Так, можно написать простой интерфейс, где будут реализованы функции getDeviceInfo и getBatteryLevel:
public class WebAppInterface { private Context mContext;
private LocationManager locationManager; private LocationListener locationListener;
public WebAppInterface(Context context) { mContext = context;
locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
}
@JavascriptInterface
public String getDeviceInfo() {
return "Device: " + Build.MODEL + ", OS Version: " + Build.VERSION.RELEASE;
}
@JavascriptInterface
public String getBatteryLevel() {
BatteryManager batteryManager = (BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE);
int batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
return "Battery Level: " + batteryLevel + "%";
}
Затем подключить его к WebView:
webView.addJavascriptInterface(new WebAppInterface(this), "os");
В этом случае мы сможем использовать эти функции из javascript, что особенно интересно, когда есть XSS.
Так может быть выполнен любой реализованный разработчиком код, в котором тоже могут быть уязвимости.
SQLi
Мобильные приложения могут быть уязвимы к SQL-инъекциям из-за использования SQLite для хранения данных и некорректной фильтрации входных данных от пользователя.
Пример уязвимого кода:
public void search(View view) {
EditText srchtxt = (EditText) findViewById(R.id.ivi1search);
try {
Cursor cr = this.mDB.rawQuery("SELECT * FROM sqliuser WHERE user
= '" + srchtxt.getText().toString() + "'", null);
Импакт от SQLi значительно меньше, чем от уязвимости в серверной части, но может быть ощутим.
С помощью этой инъекции могут быть украдены персональные данные пользователя, выполнены подмена данных, обход локальной проверки или отказ в обслуживании клиента приложения за счет порчи или удаления данных.
Path traversal
Приложение напрямую взаимодействует с файловой системой устройства, поэтому при наличии path traversal появляется возможность чтения и перезаписи произвольных файлов, для доступа к которым у приложения есть права.
По умолчанию обычный пользователь не может посмотреть содержимое директории приложения и, как следствие, получить доступ к хранящимся там данным без наличия root-прав. Так что, если мы обнаружили path traversal, позволяющий читать любые файлы от имени приложения, это уже достаточно серьезная уязвимость.
Пример уязвимого кода:
public String readFileContent(String fileName) throws IOException {
String BASEDIR = getFilesDir().getAbsolutePath();
File file = new File(BASEDIR + "/notes/"+ fileName);
StringBuilder content = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new
FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
content.append(line).append(System.lineSeparator());
}
}
return content.toString().trim();
}
Однако критичность значительно увеличивается, если это path traversal при записи файла, так как она напрямую может привести к RCE на устройстве. Для этого достаточно перезаписать одну из библиотек, используемую приложением, на свою, которая будет содержать необходимый код.
Если интересно, как это применяется в реальной жизни, советую посмотреть репорт на Hackerone, посвященный RCE в Android-клиенте Evernote.
Insecure deserialization
При разработке большинства Android-приложений используют Java/Kotlin, и при недостаточной проверке входных данных там можно обнаружить небезопасную десериализацию данных.
Пример уязвимого кода:
private void deserialize(InputStream inputStream) throws IOException,
ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(inputStream);
MyObject obj = (MyObject) ois.readObject();
TextView outputTextView = findViewById(R.id.deserOutput);
outputTextView.setText(obj.toString());
}
В этом случае потенциальный злоумышленник сможет воздействовать на серилизуемый объект, изменить его свойство или реализацию метода, как в примере:
private static void Exploit() throws IOException {
MyObject hackedObject = new MyObject("OriginalName") {
@Override
public String toString() {
return "hacked " + UUID.randomUUID().toString();
}
};
ObjectOutputStream oos = new ObjectOutputStream(new
FileOutputStream("/tmp/exploit.bin"));
oos.writeObject(hackedObject);
oos.close();
}
В примере я изменил реализацию toString так, чтобы она возвращала значение «hacked + UID», сгенерированный случайным образом.
Хотя таким образом можно внедрить любой другой код и получить его удаленное выполнение на устройстве.
Более детальный разбор уязвимости выходит за рамки этой статьи. Для глубокого понимания рекомендую почитать, что собой представляет сериализация в java, а также — как устроен инструмент для генерации полезных нагрузок с использованием цепочки гаджетов ysoserial.
Code Execution
Возможны и стандартные уязвимости, связанные с выполнением кода.
Пример уязвимого кода:
private String executeCommand(String command) { try {
Process process = Runtime.getRuntime().exec(command);
...
Такое встречается редко, так как современный SDK содержит достаточно опций, чтобы полностью отказаться от использования exec.
Inter-process communications (IPC)
В Android есть множество встроенных функций для взаимодействия между приложениями. С одной стороны, это дает большую гибкость разработчикам при построении архитектуры приложения, с другой — значительно увеличивает поверхность атаки для злоумышленников.
Объяснение всех механизмов IPC не относится к основной теме статьи, так что расскажу лишь про основные сущности, которые используются при эксплуатации уязвимостей с IPC:
Intent — это механизм межпроцессного взаимодействия в Android, который позволяет компонентам приложения, таким как Activity, Service или BroadcastReceiver, общаться между собой или с другими приложениями.
Broadcast receivers — это компонент Android, который позволяет приложениям реагировать на системные широковещательные сообщения, например на события вроде получения SMS или смены состояния сети.
Инициировать широковещательные сообщения можно при помощи утилиты am:
am broadcast -n com.test.package/.NotificationReceiver -a package.newnotification --es "text" "YOU HAVE BEEN HACKED"
Content provider — это компонент для управления доступом к структурированным данным приложений, например к базам данных или файлам внутри директории приложения.
Deeplink — это схемы, которые позволяют приложениям открывать определенные действия через обработку ссылок, например приложение может быть запущено через ссылку типа myapp://open?param=value.
Эту категорию сложно однозначно разбить на подгруппы, как инъекции, поэтому я приведу несколько примеров уязвимостей, связанных с IPC.
Exported content provider
Одна из часто встречающихся уязвимостей в Android-приложениях — неправильно настроенные права доступа к сущностям приложения. В пример приведу Content Provider.
Когда Content Provider помечается как exported, он становится доступен для других приложений. А значит, внешние приложения могут запрашивать и модифицировать данные, если нет правильной системы контроля доступа.
В моем случае провайдер предоставлял доступ к базе данных приложения, поэтому я написал proof of concept (POC) приложения, которое обращалось к провайдеру и доставало пароли из базы данных пользователя.
Пример функции, реализующей обращение к стороннему контент-провайдеру:
public static List<Map<String, String>> getPassword(Context context) {
Uri parse = Uri.parse("content://com.test.app.contentprovider/pwds");
ArrayList arrayList = new ArrayList();
Cursor query = context.getContentResolver().query(parse, null, null, null, null);
if (query != null && query.moveToFirst()) {
do {
try {
HashMap hashMap = new HashMap();
String string = query.getString(query.getColumnIndex("pwd"));
hashMap.put("name", query.getString(query.getColumnIndex("name")));
hashMap.put("pwd", string);
arrayList.add(hashMap);
} finally {
query.close();
}
} while (query.moveToNext());
}
return arrayList;
}
При поиске похожих кейсов в багбаунти я наткнулся на репорт в программе Nextcloud. Правда, тут данные доставались с использованием ПО drozer, но смысл примерно тот же.
Local auth bypass via deeplink
Deeplink используется для обработки действия в приложении при переходе по определенному типу ссылок. Часто этим действием бывает запуск активности приложения. В некоторых случаях при обработке deeplink упускается проверка безопасности приложения, например локальная проверка авторизации пользователя.
В уязвимом приложении реализовали авторизацию по ПИН-коду:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!isPinCodeVerified()) {
Intent intent = new Intent(this, PinCodeActivity.class);
startActivity(intent); finish();
} else {
setContentView(R.layout.activity_main);
}
}
Однако приложение позволяло обращаться при помощи ссылки к другим активностям, в частности к просмотру профиля пользователя.
<activity android:name=".ProfileActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="www.example.com" android:path="/profile" />
</intent-filter>
</activity>
Причем в .ProfileActivity проверку isPinCodeVerified() забыли реализовать. Для обхода локальной авторизации достаточно открыть в браузере ссылку `https://host[.]com/account`.
Или запустить приложение через deeplink с использованием утилиты am:
am start –n com.package.example –d "https://host.com/account”
Information leak via URL-scheme
Еще одна уязвимость связана с URL-схемами — когда приложение передает чувствительную информацию в открытом виде при помощи URL-scheme.
Проблема в том, что любое приложение может зарегистрировать обработку любой URL-cхемы. Мы можем написать свое приложение, которое будет обрабатывать необходимую URL-схему, например testapp://createtask:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.evil.app">
<application
android:allowBackup="true"
...
>
<activity android:name=".MailtoAppActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="testapp" android:host="createtask" />
</intent-filter>
</activity>
И оно позволит перехватить данные при обращении по URL-схеме от исходного приложения.
Так, на Hackerone наткнулся на account takeover в приложении Shopify c использованием данной уязвимости. C подробностями можете ознакомиться в самом репорте.
Binary vulns
Мобильные приложения под капотом могут использовать нативные библиотеки, зачастую написанные на языках C/C++, небезопасных с точки зрения работы с памятью.
Это открывает дорогу к появлению таких уязвимостей, как:
Buffer Overflow Integer Overflow.
Use-After-Free.
Double-free.
Так что любители pwn-категории непременно должны оценить.
Для полноценного погружения в эту категорию вряд ли хватит и нескольких статей, поэтому лучше приведу примеры обнаруженных in-the-wild-уязвимостей:
Начало работы
Читая про уязвимости, большую поверхность атаки и другие особенности, кто-то мог решить, что начать исследовать мобилки сложно. Я же думаю, что тут главное — начать. Поэтому собрал то, что может помочь на старте.
Чтоб получить начальные знания и изучить теорию, стоит посмотреть на OWASP и их MASTG и MASVS. Также я бы советовал обратить внимание на бесплатный курс от Mobile hacking lab. На GitHub можно найти подборки ресурсов с разборами заданий, репортов с багбаунти и другим контентом, который сможет помочь в обучении (AST, one more repo).
После получения базовых знаний стоит перейти к практике.
Первое, что нужно для тестирования на безопасность мобильных приложений, — это устройство. На физическом устройстве смотреть приложения в разы удобнее, но начинать проверку Android-приложений можно и с Android-эмулятора, чего не скажешь про iOS.
Из опыта могу посоветовать:
Genymotion — легкий и быстрый эмулятор, которого за глаза хватает для теста приложения.
Android studio — полноценная среда для разработки и отладки. Встроенный эмулятор — лишь малая часть его возможностей, так что этот софт гораздо тяжелее и требует больше вычислительных ресурсов. Подойдет, если вы захотите писать POC для уязвимости с вектором malware app.
Если вы планируете использовать физическое устройство, на нем предварительно нужно получить root-доступ и включить Android Debug Bridge через настройки разработчика.
При работе с мобильными приложениями стоит обратить внимания на эти утилиты:
JADX — инструмент для декомпиляции Android-приложений (APK-файлов), который преобразует байт-код Dalvik / ART (DEX) обратно в читаемый исходный код на Java, а также позволяет исследовать структуры приложений.
Frida — инструмент для динамического анализа и отладки, который позволяет интерцептировать и модифицировать выполнение кода в реальном времени на устройствах.
Objection — фреймворк на основе Frida, который упрощает процесс обхода защиты мобильных приложений.
AM (activity manager) — это Android-команда для управления активностями и другими компонентами приложений. Она позволяет через командную строку на устройстве запускать, останавливать приложения и взаимодействовать с ними.
ADB (Android debug bridge) — это командная утилита, которая позволяет взаимодействовать с Android-устройством из компьютера, выполняя отладку, установку приложений, управление файловой системой и выполнение команд в терминале.
MobSF — opensource (SAST/DAST) сканер уязвимостей для мобильных приложений. Что-то серьезное он вряд ли найдет, но подсветить узкие моменты может.
После того, как запаслись стартовым инструментарием и девайсом, идем набивать руку. В этом помогут всякого рода лабораторные и СrackMe:
Попробовать свои силы на реальных приложениях помогут багбаунти-программы. Отточить навыки можно на платформе BI.ZONE Bug Bounty, ведь там достаточно публичных программ, в скоупе которых есть мобильные приложения.
Исследование безопасности мобильных приложений открывает возможности для карьеры и помогает бизнесу создавать качественные и надежные продукты. Надеюсь, эта статья поможет кому-то из читателей обратить внимание на направление мобильных приложений и даст толчок к исследованиям в этой сфере.
Автор: Сергей Арефьев, специалист отдела анализа защищенности приложений