
Dans un
article précédent, j'ai fait une revue sur OWASP Mobile TOP 10 et puis je n'ai pas eu de cas approprié pour démontrer la nécessité de protéger le code source. Un cas intéressant de démonstration n'est apparu que récemment et qui s'intéresse à notre expérience de contournement des contrôles environnementaux, passons sous le chat.
Lors de l'évaluation de la performance de l'un des projets, notre équipe s'est immédiatement rendu compte que l'affaire ne serait pas facile. Les développeurs ont bien abordé la question de la sécurité de l'information dans le programme et ont mis en place des contrôles sur l'état de l'environnement d'exécution. La demande n'a pas démarré dans l'une des conditions suivantes:
- l'appareil a été ornière;
- un émulateur a été utilisé;
- disponibilité de la connexion via USB;
- en utilisant le mode développeur.
Les développeurs n'ont pas obscurci le code source et il n'y avait pas de contrôle intégré pour la modification du code, ce qui m'a permis d'analyser les méthodes par lesquelles les contrôles ont été effectués et d'effectuer les manipulations nécessaires avec eux.
Commençons donc. Selon OWASP Mobile TOP 10, que nous utilisons comme méthodologie de test de base chez Hacken, Reverse Engineering - cette vulnérabilité comprend l'analyse de fichiers binaires pour déterminer le code source, les bibliothèques, les algorithmes, etc. Des logiciels tels que IDA Pro, Hopper, otool et d'autres outils d'ingénierie inverse peuvent donner une idée du fonctionnement interne de l'application. Cela peut être utilisé pour rechercher les vulnérabilités des applications, extraire des informations critiques telles qu'un serveur principal, des clés de chiffrement ou la propriété intellectuelle.
Pour effectuer une analyse statique de base, j'ai utilisé un outil aussi intéressant que MobSF, qui a effectué la décompilation et l'analyse statique de base. Après décompilation, je me suis intéressé aux codes java et smali du programme. Le code Java est nécessaire pour l'analyse, et nous apporterons des modifications au code smali. Plus de détails sur la relation entre smali et java peuvent être trouvés
ici .
Après avoir examiné la liste des classes, j'ai trouvé un fichier qui est chargé de vérifier la routine du téléphone (voir Figure 1) - rootingcheck / RootBeerNative.java.
Fig. 1. Liste des classes d'applicationAprès avoir analysé la classe, il est devenu clair que nous devons chercher plus loin les appels aux fonctions checkForRoot () et setLogDebugMessage () (voir la figure 2).
Fig. 2. Le code source de la classe pour vérifier la routineEn utilisant la commande grep, nous obtenons les résultats suivants, qui nous montrent quels fichiers contiennent l'appel aux méthodes checkForRoot () et setLogDebugMessage ().
Résultats de la recherche:root @ kali: ~ / Desktop # grep -nr 'RootBeerNative' ******** _ v_0.9.2 /
********_v_0.9.2/smali/rootingcheck/RootBeerNative.smali:1:.class public Lrootingcheck/RootBeerNative; ********_v_0.9.2/smali/rootingcheck/RootBeerNative.smali:17: sput-boolean v0, Lrootingcheck/RootBeerNative;->?:Z ********_v_0.9.2/smali/rootingcheck/RootBeerNative.smali:28: sput-boolean v0, Lrootingcheck/RootBeerNative;->?:Z ********_v_0.9.2/smali/rootingcheck/RootBeerNative.smali:57: sget-boolean v0, Lrootingcheck/RootBeerNative;->?:Z ********_v_0.9.2/smali/o/CM.smali:591: new-instance v1, Lrootingcheck/RootBeerNative; ********_v_0.9.2/smali/o/CM.smali:593: invoke-direct {v1}, Lrootingcheck/RootBeerNative;-><init>()V ********_v_0.9.2/smali/o/CM.smali:685: new-instance v0, Lrootingcheck/RootBeerNative; ********_v_0.9.2/smali/o/CM.smali:687: invoke-direct {v0}, Lrootingcheck/RootBeerNative;-><init>()V ********_v_0.9.2/smali/o/CM.smali:689: invoke-static {}, Lrootingcheck/RootBeerNative;->?()Z ********_v_0.9.2/smali/o/CM.smali:753: new-instance v4, Lrootingcheck/RootBeerNative; ********_v_0.9.2/smali/o/CM.smali:755: invoke-direct {v4}, Lrootingcheck/RootBeerNative;-><init>()V ********_v_0.9.2/smali/o/CM.smali:764: invoke-virtual {v4, v3}, Lrootingcheck/RootBeerNative;->checkForRoot([Ljava/lang/Object;)I ********_v_0.9.2/smali/o/xZ$5.smali:257: new-instance v1, Lrootingcheck/RootBeerNative; ********_v_0.9.2/smali/o/xZ$5.smali:259: invoke-direct {v1}, Lrootingcheck/RootBeerNative;-><init>()V ********_v_0.9.2/smali/o/xZ$5.smali:261: invoke-static {}, Lrootingcheck/RootBeerNative;->?()Z
root @ kali: ~ / Desktop # grep -nr 'setLogDebugMessages' ******** _ v_0.9.2 / ********_v_0.9.2/smali/o/CM.smali:599: invoke-virtual {v1, v0}, Lrootingcheck/RootBeerNative;->setLogDebugMessages(Z)I ********_v_0.9.2/smali/o/CM.smali:761: invoke-virtual {v4, v0}, Lrootingcheck/RootBeerNative;->setLogDebugMessages(Z)I
Mais ce n'étaient pas tous des chèques. Après avoir analysé la classe MainActivity.java, nous avons trouvé des appels de fonction où les chaînes «su», «test-keys» et «which» sont passées, à l'aide desquelles la vérification est effectuée (voir Fig. 3).
Fig.3. Contrôles de routineEt encore une fois, avec la commande grep, nous regardons dans les fichiers smali pour vérifier la routine:
Résultats de la recherche:root @ kali: ~ / Desktop # grep -nr 'su' ******** _ v_0.9.2 / ********_v_0.9.2/smali/o/CM.smali:443: const-string v2, "su" ********_v_0.9.2/smali/o/CM.smali:706: const-string v2, "su" ********_v_0.9.2/smali/o/xZ$5.smali:172: const-string v1, "su" ********_v_0.9.2/smali/o/xZ$5.smali:347: const-string v0, "su"
root @ kali: ~ / Desktop # grep -nr 'test-keys' ******** _ v_0.9.2 / ********_v_0.9.2/smali/o/xZ$5.smali:141: const-string v1, "test-keys" ********_v_0.9.2/smali/o/xZ$5.smali:374: const-string v0, "test-keys"
root @ kali: ~ / Desktop # grep -nr 'qui' ******** _ v_0.9.2 / ********_v_0.9.2/smali/o/CM.smali:437: const-string v2, "which"
Dans l'article, je ne montrerai qu'une des modifications trouvées des contrôles de routine. Après une petite manipulation, à savoir changer 1 à 0, les vérifications de routine ont été supprimées.
Fig. 4. La valeur de la variable est égale à un si le téléphone est enraciné
Fig. 5. Maintenant, la valeur de la variable est nulle si le téléphone est enracinéAprès cela - le programme peut être assemblé, signé avec votre clé de libération et exécuté sur un téléphone mobile. Mais sinon pour deux MAIS! À savoir:
- Vérification de la connexion USB;
- vérification de l'inclusion du mode développeur - la connexion USB et le mode développeur inclus permettent une analyse dynamique.
La vérification du mode développeur est désactivée de la même manière que la vérification de routine - en changeant l'unité à zéro dans les vérifications
Dans la classe MainActivity.java, nous trouvons la ligne qui est responsable de la vérification du mode développeur (voir la figure 6). Après cela, recherchez les fichiers dans lesquels la chaîne «development_settings_enabled» est présente et modifiez la vérification - changez 1 en 0 (voir Fig. 7 et 8).
Fig. 6. Vérifiez si le mode développeur est activé sur le téléphoneRésultats de la recherche:grep -nr "development_settings_enabled" ******** _ v_0.9.2 \ Binary file ********_v_0.9.2\/build/apk/classes.dex matches ********_v_0.9.2\/smali/o/xZ$1.smali:49: const-string v1, "development_settings_enabled"
Fig. 7. L'endroit où vous devez effectuer la modification
Fig. 8. ModificationAprès toutes les manipulations, vous pouvez exécuter le programme en mode développeur.
Ensuite, désactivez la vérification de la connexion USB. Cette vérification se trouve dans la classe MainActivity.java (voir la figure 9). Sans grep, nous trouvons la ligne dans MainActivity.smali, nous trouvons la ligne qui contient la ligne avec la vérification USB - android.hardware.usb.action.USB_STATE. Après cela, dans le code smali, nous modifions la ligne à toutes les autres autorisations qui retournent «false» (voir Fig. 10).
Fig. 9. Vérifiez la connexion USB dans le code MainActivity.java
Fig. 10. La ligne de code à supprimer dans MainActivity.smaliReste maintenant à générer votre clé de version et à signer l'application avec. Cela se fait comme suit:
- Vous devez installer deux applications: Keytool et Jarsinger.
- Exécutez la commande pour assembler l'application:
- apktool b C: \ Users \ User \ Desktop \ ********
- Suivant: cd ******** \ dist \
- Suivant: Keytool.exe -genkey -alias key.keystore -keyalg RSA -validity 20000 -keystore key.keystore
- Suivant: Jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore key.keystore ********. Apk key.keystore
- Suivant: jarsigner -verify -verbose -certs ********. Apk
Ici, en principe, toutes les manipulations sont terminées. Maintenant, nous installons l'application sur le téléphone à l'aide d'Adb Install ou à partir du répertoire du smartphone et vous pouvez effectuer des tests de vulnérabilité dynamiques.
Après avoir installé l'application, exécutez-la (voir Fig. 11 et Fig. 12).
ConclusionsDans un exemple pratique, j'ai montré comment vous pouvez désactiver certaines vérifications sur l'état du runtime. De plus, avec l'aide d'autres outils, nous avons effectué une analyse de la vulnérabilité, mais c'est une autre histoire ...
Quelle attitude négligente envers la protection du code peut conduire à:
- ce sont des contournements de certains chèques qui sont intégrés dans le programme
- mise en œuvre de code tiers, après quoi le programme peut être publié et utilisé comme malveillant
Comment puis-je me protéger? Chez
ByteCode, nous avons décidé de ne pas réinventer la roue et
avons suggéré que le client obscurcisse le code source et utilise des fonctions qui vérifient la modification du code source.
PS
Vous pouvez utiliser une méthode d'analyse plus avancée - c'est le débogage smali. Vous pouvez en savoir plus à ce sujet dans le
manuel .
Un peu pour référence, j'ai formulé une liste de lignes qui est utilisée pour vérifier la routine:
- «Clés de test»;
- "/system/app/Superuser.apk";
- "/ sbin / su";
- "/ system / bin / su";
- "/ system / xbin / su";
- "/ data / local / xbin / su";
- "/ data / local / bin / su";
- "/ system / sd / xbin / su";
- "/ system / bin / fail-safe / su";
- "/ data / local / su";
- "/ su / bin / su";
- "/ system / xbin / which";
- «Su»;
- "Lequel".