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

Мобильные приложения активно используют данные, а значит, нуждаются в грамотной защите. Поэтому за последние несколько лет значительно выросло количество проектов, связанных с анализом их защищенности. Заинтересованность компаний в безопасности мобильных приложений можно заметить и на рынке багбаунти. Например, на 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 можно найти подборки ресурсов с разборами заданий, репортов с багбаунти и другим контентом, который сможет помочь в обучении (ASTone 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, ведь там достаточно публичных программ, в скоупе которых есть мобильные приложения.

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


Автор: Сергей Арефьев, специалист отдела анализа защищенности приложений

Комментарии (0)