Présentation
À la mi-avril, nous avons
publié des informations sur le cheval de Troie
Android.InfectionAds.1 , qui exploitait plusieurs vulnérabilités critiques d'Android. L'un d'eux, CVE-2017-13156 (également connu sous le nom de
Janus ), permet aux logiciels malveillants d'infecter les fichiers APK sans endommager la signature numérique. L'autre est CVE-2017-13315. Il donne au cheval de Troie des privilèges étendus, afin qu'il puisse installer et désinstaller des applications indépendamment de l'utilisateur. Une analyse détaillée d'
Android.InfectionAds.1 est disponible dans
notre bibliothèque de virus ; pendant que nous sommes ici, nous allons aborder la vulnérabilité CVE-2017-13315 et voir ce qu'elle fait.
CVE-2017-13315 appartient au groupe de vulnérabilités baptisé EvilParcel. On les retrouve dans différentes classes de systèmes Android. Des erreurs dans ces classes permettent de substituer des informations lors de l'échange de données entre les applications et le système. Les logiciels malveillants qui exploitent les vulnérabilités EvilParcel se voient ainsi accorder des privilèges plus élevés et deviennent capables de ce qui suit:
- installer et supprimer des applications avec toutes les autorisations sans confirmation des utilisateurs;
- infecter les logiciels installés sur l'appareil et remplacer les originaux propres par des copies infectées lorsqu'ils sont utilisés avec d'autres vulnérabilités;
- réinitialiser le code PIN de l'écran de verrouillage sur les appareils Android.
A ce jour, nous connaissons 7 vulnérabilités de ce type:
- CVE-2017-0806 (erreur dans la classe GateKeeperResponse), publiée en octobre 2017;
- CVE-2017-13286 (erreur dans la classe OutputConfiguration, publiée en avril 2018;
- CVE-2017-13287 (erreur dans la classe VerifyCredentialResponse), publiée en avril 2018;
- CVE-2017-13288 (erreur dans la classe PeriodicAdvertizingReport), publiée en avril 2018;
- CVE-2017-13289 (erreur dans la classe ParcelableRttResults), publiée en avril 2018;
- CVE-2017-13311 (erreur dans la classe SparseMappingTable), publiée en mai 2018;
- CVE-2017-13315 (erreur dans la classe DcParamObject), publié en mai 2018.
Tous constituent une menace pour les appareils exécutant Android 5.0 - 8.1 sans mise à jour de sécurité de mai 2018 (ou ultérieure) installée.
Prérequis pour les vulnérabilités EvilParcel
Voyons comment les vulnérabilités EvilParcel peuvent apparaître. Tout d'abord, nous devons examiner certaines fonctionnalités des applications Android. Tous les programmes Android interagissent entre eux, ainsi qu'avec le système d'exploitation, en envoyant et en recevant des objets Intent. Ces objets peuvent contenir un nombre arbitraire de paires clé-valeur à l'intérieur d'un objet Bundle.
Lors du transfert d'une intention, un objet Bundle est converti (sérialisé) en un tableau d'octets enveloppé dans Parcel, puis désérialisé automatiquement après avoir lu les clés et les valeurs d'un bundle sérialisé.
Dans le bundle, la clé est la chaîne et la valeur peut être presque n'importe quoi. Par exemple, il peut s'agir d'un type primitif, d'une chaîne ou d'un conteneur avec des types ou chaînes primitifs. Il peut également s'agir d'un objet Parcelable.
Ainsi, le Bundle peut contenir un objet de tout type qui implémente l'interface Parcelable. Pour cela, nous devons implémenter les méthodes writeToParcel () et createFromParcel () pour sérialiser et désérialiser l'objet.
Pour illustrer notre propos, créons un bundle sérialisé simple. Nous allons écrire un code qui met trois paires clé-valeur dans le bundle et le sérialise:

Figure 1. Structure d'un objet bundle sérialisé
Notez les caractéristiques spécifiques de la sérialisation de bundle:
- toutes les paires clé-valeur sont écrites séquentiellement;
- le type de valeur est indiqué avant chaque valeur (13 pour le tableau d'octets, 1 pour l'entier, 0 pour la chaîne, etc.);
- la taille des données de longueur variable est indiquée avant les données (longueur pour la chaîne, nombre d'octets pour le tableau);
- toutes les valeurs sont alignées sur 4 octets.
Toutes les clés et valeurs sont écrites dans le Bundle de manière séquentielle de sorte que lors de l'accès à une clé ou une valeur d'un objet Bundle sérialisé, ce dernier se désérialise entièrement, initialisant également tous les objets Parcelable contenus.
Alors, quel pourrait être le problème? Le problème est que certaines classes système qui implémentent Parcelable peuvent contenir des erreurs dans les méthodes createFromParcel () et writeToParcel (). Dans ces classes, le nombre d'octets lus dans createFromParcel () sera différent du nombre d'octets écrits dans writeToParcel (). Si vous placez un objet de cette classe à l'intérieur d'un bundle, les limites des objets à l'intérieur du bundle changeront après la resérialisation. Cela crée les conditions pour exploiter une vulnérabilité EvilParcel.
Voyons un exemple d'une classe qui contient cette erreur:
class Demo implements Parcelable { byte[] data; public Demo() { this.data = new byte[0]; } protected Demo(Parcel in) { int length = in.readInt(); data = new byte[length]; if (length > 0) { in.readByteArray(data); } } public static final Creator<Demo> CREATOR = new Creator<Demo>() { @Override public Demo createFromParcel(Parcel in) { return new Demo(in); } }; @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeInt(data.length); parcel.writeByteArray(data); } }
Si la taille du tableau de données est 0, lors de la création d'un objet, un int (4 octets) sera lu dans createFromParcel () et deux int (8 octets) seront écrits dans writeToParcel (). Le premier int sera écrit en appelant explicitement writeInt. Le deuxième entier sera écrit lors de l'appel de writeByteArray (), car la longueur du tableau est toujours écrite avant le tableau dans Parcel (voir la figure 1).
Les situations où la taille du tableau de données est égale à 0 sont assez rares. Mais même lorsque cela se produit, le programme continue de fonctionner, si vous ne transmettez qu'un seul objet sérialisé à la fois (dans notre exemple, l'objet Demo). Par conséquent, de telles erreurs ont tendance à passer inaperçues.
Nous allons maintenant essayer de placer un objet Demo avec une longueur de tableau nulle dans le bundle:

Figure 2. Résultat de l'ajout d'un objet de démonstration de longueur nulle à l'ensemble
Nous sérialisons l'objet:

Figure 3. L'objet Bundle après la sérialisation
Essayons maintenant de le désérialiser:

Figure 4. L'objet Bundle après la désérialisation
Qu'obtenons-nous? Jetons un coup d'œil au fragment de parcelle:

Figure 5. Structure des parcelles après désérialisation du paquet
Sur les figures 4 et 5, nous voyons qu'au lieu de deux int, un int a été lu dans la méthode createFromParcel pendant la désérialisation. Par conséquent, toutes les valeurs suivantes du bundle ont été lues incorrectement. La valeur 0x0 à 0x60 a été lue comme la longueur de la clé suivante. La valeur 0x1 à 0x64 a été lue comme une clé. La valeur 0x31 à 0x68 a été lue comme un type de valeur. Parcel n'a aucune valeur avec le type 0x31, donc readFromParcel () signale minutieusement une exception.
Comment cela peut-il être utilisé dans la vie réelle et devenir une vulnérabilité? Voyons voir! L'erreur ci-dessus dans les classes système Parcelable permet la création d'ensembles qui peuvent différer lors de la première et des désérialisations répétées. Pour le démontrer, nous allons modifier l'exemple précédent:
Parcel data = Parcel.obtain(); data.writeInt(3);
Ce code crée un bundle sérialisé qui contient une classe vulnérable. Voyons maintenant ce que nous obtenons après avoir exécuté ce code:

Figure 6. Création d'un bundle avec une classe vulnérable
Après la première désérialisation, ce bundle contiendra les clés suivantes:

Figure 7. Après désérialisation d'un bundle avec une classe vulnérable
Maintenant, nous allons à nouveau sérialiser le bundle, puis le désérialiser à nouveau et regarder la liste des clés:

Figure 8. Résultat de la resérialisation et de la désérialisation d'un bundle avec une classe vulnérable
Que voyons-nous? Le Bundle contient maintenant la clé cachée (avec la valeur de chaîne "Salut!"), Qui n'était pas là auparavant. Examinons le fragment de colis de ce bundle pour voir pourquoi cela s'est produit:

Figure 9. Structure de parcelle d'un objet Bundle avec une classe vulnérable après deux cycles de sérialisation et de désérialisation
C'est là que nous pouvons voir tout l'intérêt des vulnérabilités EvilParcel. Nous pouvons spécifiquement créer un Bundle qui contiendra une classe vulnérable. Changer les limites de cette classe permettra de placer n'importe quel objet dans ce Bundle; par exemple, une intention, qui n'apparaîtra dans le bundle qu'après la deuxième désérialisation. Cela permet de masquer une intention des mécanismes de sécurité du système d'exploitation.
Exploiter EvilParcel
Android.InfectionAds.1 a exploité CVE-2017-13315 pour installer et supprimer des logiciels indépendamment des propriétaires d'appareils. Mais comment?
En 2013, l'
erreur 7699048 , également appelée Launch AnyWhere, a été découverte. Il a permis à des applications tierces de démarrer des activités arbitraires au nom d'un utilisateur système plus privilégié. Voir le schéma ci-dessous pour le mécanisme d'action:

Figure 10. Fonctionnement de l'erreur 7699048
Une application exploitante peut utiliser cette vulnérabilité pour implémenter le service Account Authenticator, conçu pour ajouter de nouveaux comptes au système d'exploitation. L'erreur 7699048 aide les activités de lancement d'exploit à installer, supprimer, remplacer des applications, ainsi qu'à réinitialiser le code PIN ou le verrouillage de modèle et causer beaucoup plus de problèmes.
Google Inc. a éliminé cette violation en interdisant le lancement d'activités arbitraires depuis AccountManager. Désormais, AccountManager permet uniquement le lancement d'activités provenant de la même application. À cette fin, il vérifie et fait correspondre la signature numérique du logiciel qui a initié l'activité avec la signature de l'application où se trouve l'activité. Cela ressemble à ceci:
if (result != null && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) { int authenticatorUid = Binder.getCallingUid(); long bid = Binder.clearCallingIdentity(); try { PackageManager pm = mContext.getPackageManager(); ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId); int targetUid = resolveInfo.activityInfo.applicationInfo.uid; if (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authenticatorUid, targetUid)) { throw new SecurityException( "Activity to be started with KEY_INTENT must " + "share Authenticator's signatures"); } } finally { Binder.restoreCallingIdentity(bid); } }
Il semblerait que le problème soit résolu, mais ce n'est pas aussi simple que cela. Il s'est avéré que la vulnérabilité bien connue, EvilParcel CVE-2017-13315, fournit une solution de contournement! Comme nous le savons déjà, après avoir corrigé Launch AnyWhere, le système vérifie la signature numérique de l'application. S'il est vérifié avec succès, le bundle est transféré vers IAccountManagerResponse.onResult (). Dans le même temps, onResult () est appelé via le mécanisme IPC, de sorte que le bundle est à nouveau sérialisé. Lors de l'implémentation de onResult (), les événements suivants se produisent:
private class Response extends IAccountManagerResponse.Stub { public void onResult(Bundle bundle) { Intent intent = bundle.getParcelable(KEY_INTENT); if (intent != null && mActivity != null) {
Ensuite, le bundle extrait la clé d'intention et l'activité est lancée sans aucune vérification.
Ainsi, pour lancer une activité arbitraire avec des privilèges système, il vous suffit de créer un Bundle avec le champ Intent caché lors de la première désérialisation et apparaissant lors de la désérialisation répétée.
Comme nous le savons déjà, les vulnérabilités EvilParcel peuvent réellement effectuer cette tâche.
À l'heure actuelle, toutes les vulnérabilités connues de ce type ont été corrigées au sein des classes Parcelable vulnérables. Cependant, de nouvelles classes vulnérables pourraient apparaître à l'avenir. La mise en œuvre du bundle et le mécanisme d'ajout de nouveaux comptes sont toujours les mêmes qu'auparavant. Ils nous permettent toujours de créer cet exploit exact lors de la détection d'anciennes ou de nouvelles classes Parcelable vulnérables. De plus, ces classes sont toujours implémentées manuellement, et le programmeur doit s'assurer que la longueur de l'objet Parcelable sérialisé reste la même, ce qui est un facteur humain avec tout ce qu'il implique. Cependant, nous espérons qu'il y aura aussi peu d'erreurs que possible et que les vulnérabilités EvilParcel ne constitueront pas une menace pour les utilisateurs d'Android.
Vous pouvez vérifier votre appareil mobile pour les vulnérabilités EvilParcel en utilisant notre
espace de sécurité Dr.Web pour Android. L'auditeur de sécurité intégré signalera les problèmes détectés et recommandera des moyens de les éliminer.