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

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.

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 :

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.

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 :

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é :

Application malveillante

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

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 :

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.

Références