12
oct.
'12

Class 0 SMS: reloaded

Publié le 12 octobre 2012

Depuis ma dernière présentation publique concernant Android (c'est-à-dire depuis la Nuit du Hack 2011, pfiou), beaucoup de personnes m'ont demandé s'il était possible d'envoyer des SMS de classe 0 sur les dernières versions de cet OS. Personnes auxquelles j'avais répondu à l'époque que la démonstration avait été réalisée sur la version 2.1, et que l'astuce ne fonctionnait plus sur les versions récentes. Néanmoins, l'idée de chercher un moyen de le faire sur les version supérieures à 2.1 me trottait en tête depuis ce temps, mais j'avoue ne pas avoir pris le temps de m'y intéresser. Jusqu'à très récemment.

SMS de classe 0: rappels

Lors de ma précédente présentation, j'avais démontré la facilité avec laquelle on pouvait créer des SMS à partir du PDU (Protocol Description Unit) et comment on pouvait se servir de cette fonctionnalité pour transformer un SMS classique en SMS de classe 0. Pour rappel, un SMS de classe 0 est un message particulier qui est destiné à s'afficher "de force" par le téléphone qui le reçoit, généralement utilisé par les opérateurs pour envoyer des notifications critiques aux utilisateurs de leur réseau ou tout simplement pour communiquer directement avec la SIM (via des systèmes peu documentés).

Ainsi, pour envoyer un SMS à partir du PDU correspondant, il fallait modifier la visibilité d'une méthode du SmsManager (sendRawPdu) afin de pouvoir l'appeler, sous réserve de posséder les bonnes permissions. Il s'agissait en fait d'une fonctionnalité offerte par le SDK Android, qui n'était pas directement exposée mais qui était toutefois présente. Cela était valable jusqu'à la version 2.1 du système. A partir de la version 2.2, le SDK a radicalement changé (du moins en ce qui concerne l'envoi de SMS), et cette méthode a disparu: impossible d'envoyer des SMS "craftés" à partir de PDUs ! Damned. Rien sur Internet expliquant comment le faire, et le code est un brin indigeste.

Services systèmes d'Android

Un jour, j'ai pris mon courage à deux mains et décidé de plonger dans le code du SDK [2], qui de fait est opensource et librement consultable, afin de trouver un moyen d'envoyer (de nouveau) ce type de message. Et par extension d'être capable de faire joujou avec les PDUs, comme au bon vieux temps. Et là, le drame: plus aucune référence de "PDU" dans les éléments offerts par le SDK. Le vide. Vraiment rien ? Eh bien après quelques recherches plus poussées, j'ai pu identifier une classe offrant une méthode nommée sendRawPdu, mais appartenant au package com.android.internal.telephony. En cherchant à savoir où elle était appelée, je suis remonté jusqu'à la classe com.android.internal.telephony.IccSmsInterfaceManager, qui propose des méthodes permettant l'envoi de messages (mais pas de PDU) et qui hérite de ISms.Stub. Attendez, "ISms", serait-ce une interface ? Jetons-y un œil.

Il s'agit effectivement d'une interface munie d'un Stub qui sert principalement de wrapper de service, tel que précisé dans la documentation:

<quote>Interface for applications to access the ICC phone book. The following code snippet demonstrates a static method to retrieve the ISms interface from Android:

private static ISms getSmsInterface()
throws DeadObjectException *
IServiceManager sm = ServiceManagerNative.getDefault();
ISms ss;
ss = ISms.Stub.asInterface(sm.getService("isms"));
return ss;
*

</quote>

Il y est fait mention d'un ServiceManagerNative, qui gère des services. En réalité, c'est un service natif qui gère l'envoi des SMS, appelé au travers d'une couche bien définie de classes permettant de vérifier les permissions et de structurer le message à envoyer. Il existe d'ailleurs d'autres services que ce "isms": "phone","content", etc ... L'utilisation de services permet de cloisonner les fonctionnalités mais surtout de limiter les fonctions exportées: l'interface restreint leur usage et complique l'implémentation d'autres fonctionnalités.

Là où ça devient intéressant, c'est que la classe SMSDispatcher est instanciée par la classe `IccSmsInterfaceManager <http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.7_r1/com/android/internal/telephony/IccSmsInterfaceManager.java?av=f>`_, et il semble donc possible de pouvoir récupérer une instance de cette classe. J'ai de suite pensé l'instancier via l'application, mais un problème est survenu: le contexte d'instanciation n'est pas bon. Seule une instance de la classe com.android.internal.telephony.Phone peut retourner l'instance unique et utilisable d'IccSmsInterfaceManager, via la méthode getIccSmsInterfaceManager. Et c'est là que ça se complique.

Récupération de l'instance système Phone

Le problème reste entier: sans une instance de la classe Phone, impossible d'atteindre une instance de SMSDispatcher, on peut donc oublier l'envoi de SMS via PDU. Cependant, une classe offre la possibilité de récupérer l'instance Phone courante: PhoneFactory. Cette classe est encore une fois interne et n'est pas exposée via le SDK "standard" offert par Google. Elle offre une méthode getDefaultPhone() qui retourne une instance de Phone, avec toutefois un bémol: un appel à cette méthode ne réussit que si le code appelant est exécuté au sein du processus com.android.phone, le processus dédié (et unique) à la gestion de la téléphonie sur le smartphone. Et un souci de plus.

Pour pouvoir exécuter du code dans le contexte du processus com.android.phone, il faut que l'application respecte les contraintes suivantes: * elle doit être signée avec le même certificat que l'application créant le processus com.android.phone * le manifest doit spécifier le processus dans lequel une application, activité ou service va s'exécuter

Par conséquence, notre application doit être une application système (com.android.phone étant mis en place par le système, et signée avec le certificat système) et doit forcer son code à être exécuté dans le contexte de com.android.phone. Heuu, ça s'annonce difficile.

Plusieurs questions se posent: * Comment créer une application système ? * Comment exécuter une activité dans le contexte du processus com.android.phone ?

Une fois ces questions résolues, l'appel de sendRawPdu sera un jeu d'enfant.

Création d'une application système

Nous allons devoir créer une application système, cela implique plusieurs choses: * être capable d'utiliser tous les éléments du SDK Android, et notamment les classes internes (com.android.internal.*) * être capable de signer l'application de manière à ce qu'elle soit reconnue comme application système * être capable de l'installer sur un smartphone

L'élément le plus bloquant pour la création d'application système reste la phase de signature de l'APK: celui-ci doit être signé avec le même certificat que les autres applications systèmes. Sur une version Android standard, c'est quasi impossible à moins d'avoir la clef privée. Par contre sur des mods d'Android comme CyanogenMod, la clef privée du certificat est publique et peut être utilisée pour signer une application et l'installer comme application système sur une rom CyanogenMod.

Le développement avec Eclipse est plus sport, la version du framework Android (android.jar) fournie dans le SDK ne contient pas les APIs internes. A moins d'utiliser la réflexion Java à gogo, s'interfacer avec les APIs internes est impossible en l'état. C'est ce que je pensais jusqu'à ce que je tombe sur ce tutorial très bien fait, expliquant comment intégrer dans le fichier android.jar les classes internes récupérées grâce à jd-gui, et comment patcher le plugin ADT afin d'autoriser l'utilisation des APIs internes. Il est assez bien fait, mais je vais vous donner une méthode plus rapide pour arriver au même résultat.

Premièrement, il vous faudra télécharger les différentes versions du SDK Android qui incluent les classes internes, qui sont disponibles sur github. Ensuite, pour chaque dossier dans le SDK, copier le contenu dans un dossier avec pour nom android-X-internals, et insérer dans chacun d'entre eux les fichiers issus de github:

# cd <SDK>/platforms
# cp -rf ./android-7 ./android-7-internals
# cp -rf ./android-8 ./android-8-internals
# cp -rf ./android-9 ./android-9-internals
# cp -rf ./android-10 ./android-10-internals
# wget "https://github.com/inazaruk/android-sdk/tarball/master" -O /tmp/platforms.tgz
# tar xvzf /tmp/platforms.tgz

Il ne reste plus qu'à patcher le plugin ADT, tel qu'indiqué sur cette page. Une fois cela fait, relancer Eclipse et vous devriez voir apparaître de nouvelles versions du SDK d'android, avec un numéro de SDK négatif ...

... ainsi que la restriction sur les APIs internes, qui doit être patchée (com/andoird/internax/* à la place de com/android/internal/*):

Une fois notre application développée, nous allons pouvoir la signer et l'installer sur notre smartphone (utilisant CyanogenMod si vous avez suivi):

# signapk.sh MonApp.apk /tmp/MonApp-signed.apk
# adb shell mount -o rw,remount /system
# adb push /tmp/MonApp-signed.apk /system/app

J'utilise signapk.sh, un script basé sur signapk.jar et les certificats de CyanogenMod (Gingerbread, CM 7.2). Ce script effectue basiquement la commande suivante:

java -jar signapk.jar platform.x509.pem platform.pk8 Application.apk Application-signed.apk

Après cette dernière étape, notre application système sera installée et reconnue par Android en tant que tel. Bon, reste à développer une application d'envoi de SMS classe 0 maintenant que l'on possède un IDE et un SDK améliorés.

Utilisation de la réflexion pour l'envoi de SMS via PDU

Faisons le point. On a un éditeur Eclipse avec ADT patché et au moins un SDK qui intègre les classes com.android.internal.*. On sait aussi déployer une application système. Au boulot.

Pour commencer, on récupère l'instance de Phone (supposons que cela soit exécuté dans le bon processus, ce point sera abordé par la suite):

Phone phone = PhoneFactory.getDefaultPhone();

Puis on récupère une instance de IccSmsInterfaceManager:

f = IccSmsInterfaceManager.class.getDeclaredField("mDispatcher");
f.setAccessible(true);
SMSDispatcher sms_disp = (SMSDispatcher)f.get(ismsm);

Et on utilise un brin de réflexion Java pour appeler la bonne méthode, en transformant au passage un message SMS classique en SMS de classe 0 (via la manipulation des PDUs)

byte[] b = new byte[0];
SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(
    null, phoneNumber, message,false
);

/* change class to Class 0 */
size = (int)pdus.encodedMessage[2];
size = (size/2) + (size%2);
pdus.encodedMessage[size+5] = (byte)0xF0;

/* send raw pdu */
Method m = SMSDispatcher.class.getDeclaredMethod("sendRawPdu", b.getClass(), b.getClass(), PendingIntent.class, PendingIntent.class);
m.setAccessible(true);
m.invoke(sms_disp, pdus.encodedScAddress, pdus.encodedMessage, null, null);

Et voilà, rien de bien compliqué niveau code. Une fois le tout assemblé avec la gestion des exceptions:

/* Sends class 0 SMS */
private boolean sendSMS(String phoneNumber, String message)
*
    int size;
            Field f;
    SMSDispatcher sms_disp;

    Phone phone = PhoneFactory.getDefaultPhone();

    /* Get IccSmsInterfaceManager */
    IccSmsInterfaceManager ismsm = phone.getIccSmsInterfaceManager();

    try {
            f = IccSmsInterfaceManager.class.getDeclaredField("mDispatcher");
            f.setAccessible(true);
            sms_disp = (SMSDispatcher)f.get(ismsm);
            if (sms_disp != null)
            {
                    byte[] b = new byte[0];
                    SmsMessage.SubmitPdu pdus =
                            SmsMessage.getSubmitPdu(
                            null, phoneNumber, message,false
                    );

                    /* change class to Class 0 */
                    size = (int)pdus.encodedMessage[2];
                    size = (size/2) + (size%2);
                    pdus.encodedMessage[size+5] = (byte)0xF0;

                    /* send raw pdu */
                    Method m = SMSDispatcher.class.getDeclaredMethod("sendRawPdu", b.getClass(), b.getClass(), PendingIntent.class, PendingIntent.class);
                    m.setAccessible(true);
                    m.invoke(sms_disp, pdus.encodedScAddress, pdus.encodedMessage, null, null);

                    return true;
            *
            else
                    return false;

    } catch (SecurityException e) *
            return false;
    * catch (NoSuchFieldException e) *
            return false;
    * catch (IllegalArgumentException e) *
            return false;
    * catch (IllegalAccessException e) *
            return false;
    * catch (NoSuchMethodException e) *
            return false;
    * catch (InvocationTargetException e) *
            return false;
    *
}

Exécution dans le contexte de com.android.phone

Le cœur du système d'envoi est développé, il ne reste plus qu'à préciser le manifeste de manière à ce que l'application soit lancée dans le contexte du processus com.android.phone. J'ai fait beaucoup de tests, et la seule configuration qui semble passer est la suivante, basée sur un identifiant d'utilisateur partagé android.uid.system et sur la définition du processus dans lequel l'activité ZeroSMS doit s'exécuter:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.android.zerosms"
      android:versionCode="1"
      android:versionName="1.0"
      android:sharedUserId="android.uid.system">
    <uses-permission android:name="android.permission.SEND_SMS"/>
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".ZeroSMS"
                  android:label="@string/app_name"
                  android:process="com.android.phone">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Il ne reste plus qu'à compiler et produire un APK non-signé, qui sera ensuite signé avec signapk, et installé avec adb, comme mentionné précédemment. Une fois installée, l'application permet l'envoi de SMS classe 0. J'ai publié le code de l'application ainsi qu'une version compilée et signée sur Github [1]. Je ne vous fait pas de démo, mais je vous laisse essayer l'APK final (ZeroSms-signed.apk, situé dans le dossier apks). L'application ressemble à ceci:

<center><img131><img132></center>

Quid des permissions ?

Durant mes tests (qui m'ont pris plusieurs jours) j'ai eu des difficultés à arriver à la solution décrite dans cet article, et j'ai pu notamment remarquer que le fait de passer par l'envoi de PDU contourne les restrictions (via les permissions) sur l'envoi de SMS. J'ai eu énormément d'erreurs de type "User 1001 has no permission android.permission.SEND_SMS", ce qui m'a bloqué pendant quelque temps, jusqu'à ce que je trouve cette parade (passer par la classe SMSDispatcher). Cela contourne notamment les vérifications faites par la classe IccSmsInterfaceManager sur la permission android.permission.SEND_SMS. Ceci dit, vu que l'application est une application système, cela ne remet pas en cause la sécurité d'Android (l'application étant installée de force via adb), mais offre un moyen de contourner les vérifications effectuées sur certaines permissions.

Conclusion

Je dois avouer que les manipulations à effectuer sur la version 2.1 et celles présentées ici sont franchement différentes et bien plus complexes sur les SDK récents. Cependant, une fois le SDK Android amélioré et le plugin ADT d'Eclipse patché, le développement est très intuitif et permet de jouer pleinement avec les composants système: téléphone, sms, etc ... Je suis sûr que ça peut ouvrir des voies vers d'autres utilisations !

Notez toutefois que l'application peut être signée pour les différents mods Android populaires, bien que dans mon exemple je l'ai fait pour CyanogenMod 7.2. Cette astuce doit fonctionner sur les versions récentes d'Android, toutefois ne disposant pas d'un téléphone sous ICS je n'en ai pas la certitude, si quelqu'un peut confirmer ça serait super.

MAJ - 12/10/2012

Versions compatibles (suites aux retours de lecteurs): * CyanogenMod version 7.2 * CyanogenMod version 9 * CyanogenMod version 10

References

`1] [ZeroSMS sur Github <https://github.com/virtualabs/ZeroSMS>`_

`2] [Code source Android 2.3.7 (Grepcode) <http://grepcode.com/snapshot/repository.grepcode.com/java/ext/com.google.android/android/2.3.7_r1/>`_

28
sept.
'12

Tout est sous contrôle

Publié le 28 septembre 2012

On vit une drôle d'époque. Sincèrement. Il suffit que tout récemment certains utilisateurs de Facebook se rendent compte (en allant déterrer leurs messages d'avant 2009) que leur vie privée n'était pas si privée que cela pour que des ministres demandent des comptes et lancent une enquête de la CNIL. Tout ça parce que des utilisateurs n'ont pas (encore) compris que mettre sur un serveur distant des données personnelles (et pour certaines confidentielles) les rendait hors de contrôle. La bonne blague. Comme si ce genre de problème ne se produisait que dans le monde virtuel, le monde numérique.

Confidentiel, vous avez dit ?

Je forme des gens à la sécurité informatique, et plus généralement à la sécurité de l'information. Et cela plusieurs semaines par an. Et ce soir, en rentrant du boulot (où je dispense une formation, comme c'est étrange), une femme vient s'asseoir sur le siège diagonalement opposé au mien, dans un train de banlieue comme il y en a tant. Un train bondé, rempli de gens éreintés. Mais contrairement aux autres personnes qui ont déjà dégainé leurs smartphones, elle sort un bloc-notes ainsi qu'un document de quelques pages sur lequel est inscrit la mention "CONFIDENTIEL" en rouge, et encadrée.

Elle fait cela sous le nez de trois personnes, et commence à annoter le document comme si de rien n'était. Confidentiel, c'est marqué. On est trois à lorgner sur le document, et elle continue d'annoter: "Expliquer le schéma, comme je l'ai indiqué", "Non.". Le nom de sa société est visible, le document concerne une proposition commerciale, et le nom du client traîne pas très loin. Du grand art, je vous dit. J'avais mon smartphone dans les mains, en deux mouvements de doigt je lance l'appareil photo, coupe le son, et prend une série de clichés (avec zoom) de ce document annoté. Comme si de rien n'était. Et personne n'a rien remarqué. Normal quoi. Un smartphone, un geek, et une position qui fait style "je ne veux pas que vous puissiez voir mon écran".

Espion en herbe

J'ai été assez étonné de ne pas voir cette personne s'offusquer, car bon trois personnes qui reluquent son document ne l'ont pas fait broncher, et encore plus de pouvoir prendre ces photos sans me faire repérer. Je connais certaines personnes qui ont eu des problèmes juste en laissant un document annoté "CONFIDENTIEL" sur leur bureau, sans que même d'autres personnes y ait eu accès. Mais non, "CONFIDENTIEL" devait être là juste pour faire joli. Et pourtant je suis sûr que cette personne fait tout de même attention à ses documents sur son ordinateur, et à ses dossiers. Qu'elle trie tout comme il faut, et fait attention aux documents qu'elle transmet et stocke dans ses emails. Enfin j'ose espérer.

Pourtant, avec un smartphone tout ce qu'il y a de plus abordable et commun, n'importe qui peut se transformer en espion et prendre des photos à l'insu de personnes, en toute discrétion. Imaginons deux minutes que je travaille chez un concurrent, et l'on pourrait envisager que je puisse me servir de cette information. D'ailleurs ce n'est pas sans vous rappeler certaines personnalités qui se sont fait avoir avec une photo de documents sous le bras ... Même Nokia peut vous faciliter la tâche, avec ses derniers modèles intégrant une stabilisation de l'image, tout en étant hype.

Dernier point, et non des moindres: il est possible de nos jours de prendre une photo d'une personne ou d'un document à l'aide d'un objet très répandu, sans que personne ne le remarque. Un peu comme les appareils photos miniatures utilisés par des supers-espions à l'époque de la guerre froide, mais en plus gros. Sans que cela n'attire l'attention. Sans un bruit. Car le célèbre "clic" peut être désactivé, autorisant les prises de vues furtives. Et ce n'est pas neuf, malheureusement.

L'accès à l'information

Vouloir protéger l'information est une noble cause, et cela passe par différents mécanismes mis en œuvres: des procédures internes, une classification des documents, ainsi qu'une politique de mots de passe solide et éprouvée. On arrête pas de le dire. Mais vous ne pourrez jamais être derrière tout le monde, et encore moins derrière les bourreaux de travail ("workaholics") qui continuent de bosser dans le train qui les mène chez eux. Parce que bon, les objectifs et la carrière sont importants. Quant à la confidentialité ...

D'ailleurs, si par le plus grand des hasards cette personne lit ceci et se reconnaît, qu'elle n'hésite pas à me contacter. Sait-on jamais.

23
juil.
'12

Reverse-engineering d'application Flex

Publié le 23 juillet 2012

Tout récemment, j'ai eu à auditer un jeu en ligne pour un client, et je m'attendai à un jeu super travaillé qui repose sur des standards connus (un jeu en HTML5/CSS3/JS comme l'excellent CutTheRope). Mais non, je me suis retrouvé face à un jeu basé sur Flash et la technologie Flex. Si vous êtes un lecteur régulier de ce blog, vous devez savoir que les applications Flash ne me rebuttent pas, bien au contraire !

La prise en main du jeu est simple, mais le dialogue avec le serveur de jeu reste très obscur (car il s'agit d'un MEUPORG, bien sûr). D'après les captures effectuées, les données respectent le format Action Message Format (AMF) défini par Adobe, j'y avais déjà touché à l'époque des débuts de Deezer mais je suis loin d'en être un grand fan. Après de plus amples recherches, j'ai découvert qu'il s'agissait d'une application Flex, dialoguant avec plusieurs serveurs de jeu via de l'AMFRPC, un protocole activement employé par Flex. J'ai donc du m'équiper.

1. Outillage

Pour analyser des applications Flash, rien de mieux qu'un décompilateur Flash de renom. Pour ma part, je n'utilise pas Flare car incompatible avec les dernières versions de Flash, mais son pendant payant et pour Windows SWFDecompiler de Sothink. Cet outil reste LA référence dans le domaine, et permet de désassembler (presque) toutes les applications Flash. A cela s'ajoute la suite SWFTOOLS, bien pratique pour extraire des informations d'applications Flash (des ressources notamment).

Pour le proxy web, j'ai opté pour Charles Web Proxy. Il s'agit encore une fois d'un outil pour Windows, mais vu que de toute façon j'utilise déjà SWFDecompiler ... Charles Web Proxy permet de faire ce que tout bon proxy web transparent doit normalement savoir faire: espionner les requêtes, autoriser des points d'arrêt et surtout être capable de décoder et d'encoder les données selon plusieurs formats, dont l'AMF. On pourrait aussi employer Burp, qui possède la même fonctionnalité.

Pour terminer, la bibliothèque Python PyAMF permet de coder rapidement des clients AMFRPC en Python, très pratique dans notre cas.

Muni de ces outils, il est alors aisé d'intercepter les appels AMFRPC, de les analyser et de comprendre le fonctionnement global du service distant.

2. Flex, services et méthodes

Les applications Flex reposent sur des endpoints dans lesquels des services sont exposés, qui implémentent des méthodes que l'application Flash peut appeler à distance. Il s'agit ici de ce qu'on appelle couramment un système d'appel de procédure distante, ou Remote Procedure Call (RPC). Ce système de service est très courant, et permet d'offrir plusieurs services sur un seul point de connexion (ou endpoint.

Du point de vue d'un pentester, un service Flex est une grosse boîte noire possédant des services qui implémentent des méthodes, qu'il va falloir découvrir. Pour cela, plusieurs solutions sont envisageables:

Le dernier point est un peu plus ardu, car nécessite le développement d'un client AMFRPC dédié. Les deux premières méthodes sont donc préférables. J'ai ainsi débuté mon analyse par une décompilation du fichier SWF contenant le coeur de l'application. Et les ennuis ont commencés: SWFDecompiler plante lâchement durant l'analyse, et impossible donc d'obtenir le code de l'application. J'ai fait de nombreuses tentatives, avec différents logiciels, sans succès. Il semblerait que SWFDecompiler ait montré ses limites. Cela signifie-t-il qu'il est impossible d'extraire quoique ce soit de ce fichier ? Que nenni (© Tixlegeek)

2.1. Flasm + strings + grep = FTW (plan B)

N'ayant pas la possibilité de désassembler l'application, le plan B consiste à extraire directement du fichier les chaînes de caractères et à essayer de déduire les services des informations s'y trouvant. Avant toute chose, on fait appel à la commande file pour identifier le type d'application:

$ file ZOMFG.swf
ZOMFG.swf: Macromedia Flash data (compressed), version 10

Il s'agit d'une application Flash >=10 compressée. Autrement dit, rien ne sert de grepper directement le contenu du fichier, c'est compressé. Il nous faut donc décompresser ce fichier, grâce à flasm:

$ flasm -x ZOMFG.swf
ZOMFG.swf successfully decompressed, 7958828 bytes
$ file ZOMFG.swf
ZOMFG.swf: Macromedia Flash data, version 10

Il est ensuite trivial d'extraire les chaînes de caractères avec strings, et de grepper le résultat à la recherche d'éléments intéressants:

$ strings ZOMFG.swf > ZOMFG.txt
$ grep ZOMFG.txt -e "some keywords here"

Il est aussi possible d'utiliser vim et ses recherches par motif, ou autre.

2.2. Analyse de flux

L'analyse de flux est relativement simple à réaliser grâce à Charles Web Proxy: celui-ci parse vraiment bien les messages AMF mais n'affiche pas tout (comme par exemple certaines listes), cependant cela reste un compagnon de choix ! Cette analyse de flux permet de mettre à jour un certain nombres de services, ainsi que de méthodes.

Il suffit d'utiliser l'application, et de noter les différents services et les méthodes associées. De plus, il peut être intéressant de repérer les différents messages et les champs associés, afin d'envisager des attaques par rejeu par la suite.

Le champ destination contient la référence au service, ici appelé "SMC". Le champ operation décrit la méthode à appeler, en l'occurrence "ExecuteServerCall".

{2.3. Enumération des {services et des méthodes associées}}

Cette dernière possibilité repose sur les résultats produits par les deux étapes précédentes: à l'aide de la première analyse, on établit une liste potentielle de services et de noms de méthodes, puis à l'aide d'un client AMFRPC maison, on teste la validité des services puis on détermine les méthodes existantes à partir des services identifiés.

Cette technique permet de déterminer de manière plus efficace les services et les méthodes associées, et permet de trouver dans certains cas des éléments non-présents dans l'application. J'ai développé dans le cadre de ce test d'application un outil semblable à DeBlaze, mais permettant de tester un service Flex distant (et aussi beaucoup moins poussé). Cet outil repose sur la bibliothèque Python PyAMF, et tente de déterminer les services offerts par un endpoint donné. Cela suppose que l'on connaisse le nom de cet endpoint, qui peut être trouvé via les deux méthodes précédentes.

Je ne peux pas dévoiler le code source de cet outil dans ce post (ooooh!), car il est soumis à des règles relativement strictes de confidentialité, propres à Sysdream. Cependant, l'algorithme de base est le suivant (aaaah !):

Pour chaque nom de service probable:
    resultat = tenter un appel à la méthode Trololololo
    Si pas d'erreur d'invocation:
        Affiche 'Service: ' + service
        Pour chaque méthode probable:
            resultat =appeler  la méthode 'methode' du service
            Si aucune erreur d'invocation ni message de méthode non trouvée:
                  Affiche '- Methode:' + methode

De cette manière, on teste l'ensemble des services possibles et des méthodes probables. On peut aussi envisager des combinaisons de nom, des changement de casse, etc ... A noter que la référence à l'endpoint est obligatoire dans les headers Flex.

3. Sessions Flex

Lors de mes tests, j'ai été rapidement rejeté par le service Flex à cause des sessions. En effet, les applications Flex font appel à une gestion de session en Java (donc un cookie JSESSIONID par défaut), et il faut gérer ce cookie dans le client AMF pour pouvoir maintenir une session correcte. De même, l'envoi des requêtes via des "messages" AMF est basé sur une numérotation qui est continuellement incrémentée. Celle-ci doit aussi être maintenue par notre client AMF.

Lors des tests, et tout particulièrement lors des tests fonctionnels, les sessions sont primordiales et bien souvent mises de côté par les outils qui ne font pour la plupart que des tests unitaires. Ce qui impose dans bien des cas l'implémentation d'un client Flex maison prenant en charge ces sessions, quand il n'y a pas d'autres éléments de cookie à prendre en compte, bien sûr.

4. Conclusion

Les tests d'applications Flex se rapprochent fortement des tests de services web, car au fond le système Flex n'est rien d'autre qu'un service web basé sur AMF (au lieu des services SOAP que l'on retrouve souvent). Sans les possibilités de découverte des services et méthodes offertes par WSDL. Je pense avoir fait le tour des principaux soucis rencontrés durant le test de ce type d'application, même si le cas évoqué ici était un brin récalcitrant. D'ailleurs à ce jour, je n'ai pas trouvé de logiciel de décompilation permettant de décompiler ce satané SWF. Si quelqu'un a une idée (ou une révélation divine), me contacter via twitter ou via gmail.



Les contenus disponibles sur ce blog sont publiés sous licence Creative Commons BY-NC-SA.
Vous pouvez réutiliser tout ou partie de ces contenus à condition de citer l'auteur et l'origine, vous ne pouvez en faire une utilisation commerciale, et enfin vous devez partager tout travail ou œuvre dérivée sous les mêmes conditions — c'est-à-dire avec la même licence d'utilisation Creative Commons.