NEW

Les pentests automatisés par IA sont arrivés chez Mobeta

Intents Android (1/2) : fonctionnement, sécurité et exemples d’attaques

Une WebView exécute un script injecté via une donnée d’Intent piégée : XSS réfléchie sur Android

Les Intents sont essentiels sur Android pour permettre la communication entre applications.
Ce sont des objets utilisés pour demander une action à un autre composant d’application.

Ils peuvent être utilisés dans plusieurs contextes :

  • Les activités
  • Les services
  • La diffusion de messages

Pour qu’un composant d’application soit accessible depuis une autre application, l’application qui expose ce composant doit définir android:exported à true.

Prenons l’exemple de l’application native Camera d’Android, qui permet de prendre des photos ou des vidéos.

L’application native Camera d’Android, qui expose son activité de capture photo

Pour permettre aux autres applications d’utiliser cette fonctionnalité, Camera expose son composant Activity en définissant android:exported="true". Ainsi, les autres applications peuvent l’appeler via l’Intent android.media.action.IMAGE_CAPTURE, déclaré dans le <intent-filter> de l’activité.

Exemple pratique

Pour illustrer comment une application utilise l’activité exposée par Camera, créons une application qui a besoin de récupérer une photo prise en direct.

On commence par déclarer notre activité :

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);

        Button homeButton = findViewById(R.id.home_button);
        homeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setAction("android.media.action.IMAGE_CAPTURE");
                intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
                startActivityForResult(intent, 42);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        Utils.showDialog(this, intent);
    }
}

Dans notre MainActivity, nous avons ajouté un bouton, puis créé un listener qui lance un nouvel Intent lorsqu’on appuie dessus.

Cet Intent utilise une action implicite pour demander à toute application qui a déclaré <intent-filter> avec android.media.action.IMAGE_CAPTURE et android:exported="true" dans son AndroidManifest.xml de gérer la requête.

En lançant l’application et en regardant les logs, on voit comment Android résout l’Intent :

Logs Android montrant la résolution de l’Intent implicite android.media.action.IMAGE_CAPTURE

Il recherche toutes les applications installées qui exposent une activité avec cet intent-filter.

Comme seule l’application Camera a déclaré cette action, Android retourne uniquement :
com.android.camera2/com.android.camera.CaptureActivity.

Notre application de démonstration affichant un bouton « Click me! » qui déclenche l’Intent de capture L’application Camera s’ouvre pour prendre une photo après résolution de l’Intent La photo revient dans notre application sous forme de Bitmap, visible dans les détails de l’Intent (Extra « data »)

Exploitation

Deux types de vulnérabilités découlent des Intents :

1. Filtrage d’entrée insuffisant

Les Intents augmentent la surface d’attaque d’une application, un peu comme une API web.
Si une application reçoit des données mal filtrées depuis une autre, cela peut mener à des failles comme XSS, SQLi, voire une RCE.

2. Intent Hijacking

Il s’agit d’utiliser le même intent-filter qu’une application légitime pour tromper l’utilisateur et intercepter des données sensibles.

Exemple :

  • L’application A dépend de l’application B pour l’authentification.
  • L’attaquant crée l’application C avec le même intent-filter que A.
  • Quand B renvoie le token d’authentification, Android laisse l’utilisateur choisir entre A et C.
  • Si l’utilisateur sélectionne C, le token est envoyé à l’application malveillante.

Nous verrons l’Intent hijacking et les deep links dans un prochain article.
Voyons d’abord un exemple concret de faille due à un mauvais filtrage d’entrée.

Exemple d’attaque par données non filtrées

Notre application dépend d’une autre pour récupérer le nom de l’utilisateur.

Elle déclare donc une activité exportée avec deux intent-filter :

Extrait d’AndroidManifest.xml déclarant une activité exportée avec deux intent-filter

L’application récupère la donnée envoyée par l’Intent et l’affiche dans une page web intégrée :

String data = getIntent().getStringExtra("data");

if (data == null) {
    data = "Invité";
}

WebView webView = findViewById(R.id.webview);

webView.getSettings().setJavaScriptEnabled(true);
webView.setWebChromeClient(new WebChromeClient());

String userInput = "<h1>Bienvenue </h1>" +
        "<p>" + data + "</p>";

webView.loadData(userInput, "text/html", "UTF-8");

Si aucune donnée n’est reçue, le nom affiché par défaut sera Invité :

L’application affiche « Invité » lorsqu’aucune donnée n’est reçue via l’Intent

Application malveillante

Créons maintenant une application qui envoie une donnée piégée :

L’application malveillante préparée pour envoyer une donnée contenant du JavaScript

En appuyant sur le bouton, on envoie une donnée contenant un script JavaScript.

L’application victime l’affiche directement dans la page web intégrée, entraînant une XSS réfléchie :

La donnée piégée contenant du JavaScript est envoyée à l’application victime La WebView de l’application victime exécute le script injecté : XSS réfléchie déclenchée

Recommandations

  • Traitez toute donnée reçue via un Intent externe comme potentiellement malveillante.
  • Filtrez et validez toutes les entrées avant de les utiliser.
  • Évitez d’exposer des activités inutilement (android:exported="false" par défaut).

Même les activités non exportées peuvent devenir une cible si elles sont déclenchées par d’autres activités exportées via des Intent contrôlables par l’utilisateur.
La surface d’attaque peut alors couvrir toutes les activités déclarées dans le AndroidManifest.xml.

Composants exportés, filtrage des entrées, surface d’attaque du manifeste : ces vérifications forment le socle d’un pentest d’application mobile.

La suite de cet article, Deep Links, App Links, attaques et mitigation, applique ces mécanismes au détournement de lien et détaille les contre-mesures.

Références

Ces techniques s’appliquent
à votre périmètre ?

Un échange de 30 min avec nos pentesters certifiés pour cadrer votre exposition. Sans engagement.

Planifier un échange

Quel est votre niveau
d’exposition réel ?

30 minutes avec un expert pour le savoir. Sans engagement.