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.

04
mai
'10

M6 replay: l'usine à fakemails/spams (rayez la mention inutile)

Publié le 04 mai 2010

M6Replay est un service de "catch-up TV", c'est à dire de TV de rattrapage. Il est réservé aux internautes français, et diffuse en streaming les épisodes de séries ainsi que d'émissions culturelles hautement intellectuelles comme "Secret Story".

Il était une fois Flash ...

Je rêvassais tranquillement sur IRC lorsqu'un lien sur le chan général a attiré mon attention: une vidéo qui faisait le buzz, et qui n'était pas posté par IvanLeF0u ! Quelle chance =). Je clique donc joyeusement sur le lien, pour aboutir sur une page qui m'envoie balader pour cause de player flash pas à jour, patati patata. Fait pas bon être sous linux. Qu'à cela ne tienne, je jette donc un oeil au code source de la page, et plonge mes mains dans le cambouis des code-monkeys de chez M6.

... et du code tout moisi...

Et là paf, je me prend une claque de suite par ce bout de code:

var hash = '';
hash+='0';
hash+='f';
hash+='6';
hash+='1';
hash+='0';
hash+='b';
hash+='4';
hash+='1';
hash+='9';
hash+='6';
hash+='c';
hash+='c';
hash+='0';
hash+='8';
hash+='9';
hash+='0';
hash+='2';
hash+='4';
hash+='d';
hash+='b';
hash+='9';
hash+='2';
hash+='7';
hash+='e';
hash+='a';
hash+='4';
hash+='b';
hash+='5';
hash+='9';
hash+='b';
hash+='b';
hash+='b';
var flashvars = {};
var configuration_url = './get_configuration.php?time='+1272922030+'&hash='+hash;
flashvars.configlink = encodeURIComponent(configuration_url);
var params = {};
params.quality = "high";
params.bgcolor = "#000000";
params.allowfullscreen = "true";
params.allowscriptaccess = "always";
var attributes = {};
attributes.id = "M6ReplayApplication";
attributes.name = "M6ReplayApplication";
swfobject.embedSWF("http://l3.player.M6.fr/swf/M6ReplayApplication.swf?dt="+(new Date()).getTime(), "flashContent", "100%", "725", "10.0.32.18", "http://l3.player.M6.fr/swf/expressInstall.swf", flashvars, params, attributes);

De suite, ça fait de la peur. Surtout l'obfuscation, à croire que les gens qui ont pondu ce code n'ont jamais entendu parler des systèmes de packing JS ou encore des techniques d'obfuscation avancées. Mais là n'est pas la question. Ce qui fait mal aux yeux, c'est la requête réalisant la récupération de la configuration: elle requiert un hash et un timestamp, certainement pour éviter une automatisation. Mais peine perdue.

Codons un petit tool pour aller chercher cette configuration:

#!/usr/bin/python

# M6Replay configuration retrieval =)
# (c) 2010 - virtualabs.fr

import re
import md5
import urllib2,urllib
import sys

# on cree quelques regexp
phash = re.compile("hash\+='([a-f0-9])';")
ptimestamp = re.compile("\./get_configuration\.php\?time='\+([0-9]+)\+'&ha")

try:
    # on recupere la page
    print '[+] Fetching m6replay.fr ...'
    req = urllib2.Request('http://www.m6replay.fr/')
    resp = urllib2.urlopen(req).read()

    # identification du timestamp et du hash
    print '[+] Extracting timestamp & hash ...'
    timestamp = ptimestamp.search(resp).group(1)
    hash = ''.join(phash.findall(resp))
    print '[i] Timestamp:%s hash:%s'%(timestamp,hash)

    # on lit la page avec le timestamp et le bon hash
    print '[+] Retrieving configuration ...'
    req = urllib2.Request('http://www.m6replay.fr/get_configuration.php?time='+timestamp+'&hash='+hash)
    configuration = urllib2.urlopen(req).read()

    # et on stocke ca dans un fichier
    config = open('configuration_v3.xml','w')
    config.write(configuration)
    config.close()
except:
    print '[!] Erreur lors de la recuperation de la configuration ...'

Et zou, il ne reste plus qu'à l'exécuter et récupérer le contenu du fichier de configuration, qui se trouve être par un pur hasard un fichier XML !

<id type="int"/>
                            <idSeq type="int"/>
                            <url_sem type="string"/>
                            <emailTo type="string"/>
                            <emailFrom type="string"/>
                            <message type="string"/>
                            <typeMail type="string"/>
                    </param>
            </service>
    </services>
[...]
    </header>
</config>

Woot ! Un fichier de description de service et des paramètres pour le player ... Que demander de plus ? On est donc ici en présence d'un service web proposant plusieurs fonctionnalités, très certainement employé par le lecteur Flash pour interagir avec la base de données. Et si on analysait un peu certaines méthodes offertes ?

Que le web service soit, et le web service fume

Le web service propose une fonctionnalité d'envoi de vidéo à un ami, fonctionnalité résumée par cette description de service:

<id type="int"/>
            <idSeq type="int"/>
            <url_sem type="string"/>
            <emailTo type="string"/>
            <emailFrom type="string"/>
            <message type="string"/>
            <typeMail type="string"/>
    </param>
</service>

Et là, c'est le drame: on peut remarquer la présence de champs tels que emailTo ou encore emailFrom ainsi que message, certainement des champs utilisés lors de la génération des emails envoyés aux amis. Le champ emailFrom semble intéressant, car il pourrait offrir la possibilité de mettre en adresse source absolument ce que l'on veut.

De plus, ce service est accessible via la méthode HTTP GET, c'est-à-dire juste en passant les différents arguments via l'url dans un navigateur :). Ce qui donne de plus très envie de tester cela. Alors testons:

http://www.m6replay.fr/pages/envoyer-a-un-ami-video.php?id=1&idSeq=1&url_sem=pouet&emailTo=virtualabs@gmail.com&emailFrom=casimir@pedobears.com&message=Coucou les n'enfants !!!!&typeMail=html

On entre cette URL dans un navigateur, et hop, il nous indique que le message a bien été envoyé ! Vérifions rapidement notre messagerie ...

Il semblerait que nous ayons bien reçu un email provenant de casimir@pedobears.com, nous invitant à consulter une vidéo sur m6replay.fr.

Quelques bonus croustillants en prime

Les entêtes sont eux aussi assez particuliers, car dévoilant des informations concernant le service qui a émis le mail:

Received-SPF: pass (google.com: domain of apache@usine.clubic.com designates 193.22.143.46 as permitted sender) client-ip=193.22.143.46;
Authentication-Results: mx.google.com; spf=pass (google.com: domain of apache@usine.clubic.com designates 193.22.143.46 as permitted sender) smtp.mail=apache@usine.clubic.com
Received: from back3b.clubic.com (localhost.localdomain [127.0.0.1])
    by back3b.clubic.com (Postfix) with ESMTP id 74C97232C080
    for <virtualabs@gmail.com>; Tue,  4 May 2010 00:02:13 +0200 (CEST)
Received: from usine.clubic.com (usine.clubic.com [193.22.143.12])
    by back3b.clubic.com (Postfix) with ESMTP id 6AF77232C07E
    for <virtualabs@gmail.com>; Tue,  4 May 2010 00:02:13 +0200 (CEST)
Received: by usine.clubic.com (Postfix, from userid 74)
    id 6732A790C2; Tue,  4 May 2010 00:02:13 +0200 (CEST)
To: virtualabs@gmail.com
Subject: Partage de la vidéo : 0 CONCERT 80' : Saison  Episode
HTTP-Posting-Client: 195.88.194.40
HTTP-Posting-URI: backstage_video.m6web.fr:80/ws.php
MIME-Version: 1.0
Content-type: text/html; charset=UTF-8
From: "casimir@pedobears.com"<casimir@pedobears.com>
Reply-To: casimir@pedobears.com
Message-Id: <20100503220213.6732A790C2@usine.clubic.com>

Notez par exemple les entêtes HTTP-Posting-Client et HTTP-Posting-URI, qui ont des valeurs assez cocasses. Il est aussi amusant de voir que les emails sont émis à partir de usine.clubic.com, et qu'ils passent le filtre anti-spam de google. Bref, une vraie machine à spam si elle tombe entre de mauvaises mains.

De plus, il est aisé d'intégrer du code HTML dans les messages envoyés, le web service ne filtrant pas celui-ci:

Et pour terminer dans la série des bonus, la détection d'envoi massif se base uniquement sur l'adresse email de destination et la session (à l'aide du cookie). Si un utilisateur malveillant effectue un envoi sur plusieurs boîtes distinctes sans stocker de cookies, alors celui-ci est indétecté.

** Conclusion **

Le web service employé par le player de M6 replay est vraiment trop laxiste en termes de sécurité, et peut servir de plate-forme de spam ou même d'envoi de fakemail à très peu de frais. Certes, le sujet du mail ne peut pas être modifié, mais il en fait un message de choix, imaginez un peu: des offres de viagra sponsorisées par M6Replay, qui viennent de M6Replay, qui ont la même tête que les mails classiques de M6Replay ... Heureusement que M6Replay n'est pas une banque.

Néanmoins, plusieurs leçons sont à tirer de cet exemple: - la conception de web service ne se fait pas à la légère - la technologie Flash est cool, mais peut être contournée - tout service peut être abusé d'une quelconque manière - le filtrage des entrées utilisateur doit être systématique

A noter toutefois qu'ils ont corrigé le script de chargement de configuration, celui-ci faisant auparavant appel à une page download.php qui prenait en paramètre un nom de fichier via un argument file, ce qui sentait bon la LFI =).



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.