Dans cet article, nous montrerons comment les conditions de concurrence affectent le système d'autorisation d'exécution d'Android.
Si vous êtes un développeur, vous avez probablement entendu parler des conditions de concurrence. Ils sont souvent associés à des opérations simultanées en arrière-plan effectuées en quelques fractions de secondes. Cependant, certaines conditions de concurrence peuvent également apparaître dans l'interface utilisateur et durer indéfiniment. Dans cet article, nous montrerons comment les conditions de concurrence affectent le système d'autorisation d'exécution d'Android.
Tout d'abord, nous devons expliquer quelques termes de base.
LaCondition de concurrence se produit si plusieurs opérations se produisent en même temps et que leur ordre affecte le résultat. Un exemple classique est celui de deux threads incrémentant la même variable. Cela semble trivial, cependant, nous devons généralement utiliser des éléments spéciaux et thread-safe pour l'implémenter correctement.
Heure du contrôle jusqu'à l'heure d'utilisation (TOCTTOU ou TOCTOU, prononcé TOCK aussi) est un type spécifique de condition de concurrence où une opération effectuée est précédée d'une vérification d'état et cet état est modifié dans le temps entre un contrôle et l'exécution réelle. Cela est souvent illustré par la vérification des privilèges des utilisateurs au moment de la connexion uniquement. Par exemple, si vous êtes administrateur au moment où vous vous connectez et que vous pouvez utiliser vos privilèges jusqu'à votre déconnexion, même si votre accès administrateur est révoqué entre-temps.
Résumons également les bases des autorisations d'exécution d'Android.
À partir d'Android 6.0 (API niveau 23), les autorisations les plus dangereuses doivent être explicitement accordées par un utilisateur au moment de l'exécution plutôt que d'un seul coup au moment de l'installation de l'application. L'élément le plus remarquable ici est une boîte de dialogue système avec les boutons DENY et ALLOW comme le montre la figure 1.
Figure 1. Boîte de dialogue d'autorisation d'exécution
Après avoir cliqué sur le bouton DENY, nous recevons PERMISSION_DENIED dans le rappel onRequestPermissionsResult et nous devrions désactiver la fonctionnalité qui dépend de cette autorisation. D'après l'extrait officiel.
De plus, l'utilisateur peut également accorder ou refuser des autorisations en utilisant l'écran Autorisations de l'application dans les paramètres de l'application. Vous pouvez voir cet écran sur la figure 2.
Figure 2. Écran des autorisations de l'application
La plupart d'entre vous pensent peut-être que le refus d'autorisation d'exécution est une fonctionnalité très simple et qu'aucun élément ne peut être brisé. Eh bien, rien ne pourrait être plus éloigné de la vérité !
Une boîte de dialogue apparaît uniquement si l'autorisation n'est pas accordée. On a donc le heure de contrôle juste avant d'afficher un dialogue. Et heure d'utilisation lorsque le bouton DENY est cliqué. Une période entre eux peut durer éternellement : l'utilisateur peut ouvrir un dialogue, puis appuyer sur le bouton Accueil ou Récent pour déplacer la tâche avec l'application en arrière-plan et revenir à tout moment plus tard.
Vérifions si le dialogue d'autorisation d'exécution est vulnérable à TOCTTOU. Pour ce faire, nous pouvons créer une activité très simple qui vérifie les autorisations réellement accordées après le retour de la boîte de dialogue. Notez qu'en dehors de la vérification standard des arguments onRequestPermissionsResult, nous appellerons Context#checkSelfPermission() pour obtenir le statut d'octroi d'autorisation actuel. N'oubliez pas de définir targetSdkVersion sur 23 ou supérieur. Le code devrait ressembler à ceci :
class MainActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) requestPermissions(arrayOf(WRITE_EXTERNAL_STORAGE), 1) } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) val checkResultTextView = findViewById<TextView>(R.id.grantResultTextView) val grantResultTextView = findViewById<TextView>(R.id.checkResultTextView) val checkPermissionResult = checkSelfPermission(WRITE_EXTERNAL_STORAGE).toPermissionResult() val grantPermissionResult = grantResults.firstOrNull()?.toPermissionResult() checkResultTextView.text = "checkSelfPermission: $checkPermissionResult" grantResultTextView.text = "onRequestPermissionsResult: $grantPermissionResult" } private fun Int.toPermissionResult() = when (this) { PERMISSION_GRANTED -> "granted" PERMISSION_DENIED -> "denied" else -> "unknown" } }
Nous pouvons maintenant effectuer un test. Pour ce faire, nous avons besoin d'un appareil ou d'un AVD avec Android 6.0 (API 23) ou plus récent. Le résultat du test est présenté sur la figure 3.
Figure 3. TOCTTOU capturé
On voit que les résultats diffèrent. L’argument onRequestPermissionsResult n’est pas valide. Le bouton DENY ne nie donc rien ! Il ne fait tout simplement rien avec le statut d'autorisation mais renvoie les résultats refusés à l'application.
Il est important de tenir compte du timing lors de la vérification de diverses choses dans le code. La mise en cache des résultats de vérification peut provoquer des bugs et des effets étranges. La vulnérabilité TOCTTOU ne dépend ni de la plateforme ni du langage de programmation, elle a donc été classée CWE-367.
Vous pouvez consulter le code source complet sur GitHub.
Le projet contient également un test d'interface utilisateur automatisé démontrant le problème.
Publié à l'origine sur www.thedroidsonroids.com le 14 décembre 2017.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!