29
déc.
'11

YouHaveDownloaded, le site qui fait de la peur !

Publié le 29 décembre 2011

Depuis quelques semaines, le site YouHaveDownloaded fait parler de lui, et tout récemment encore à propos de supposés téléchargements illégaux réalisés par des personnes au sein du Ministère de la Culture, situé rue de Valois à Paris. Nicolas Perrier s'en est d'ailleurs fait écho via un post sur son blog, et c'est via Google+ que j'ai pu en discuter avec lui.

Le protocole BitTorrent

Avant toute chose, je tiens à apporter quelques précisions sur le fonctionnement du protocole BitTorrent, et en particulier sur son architecture et sur les données stockées sur le serveur central (tracker). Le système BitTorrent repose sur un serveur central, serveur qui a donc connaissance de toutes les adresses IP des utilisateurs connectés, et qui les distribue à qui les demande. Non, il n'y a aucune authentification ni vérification faites sur les utilisateurs réclamant des adresses IP, tout au plus une limitation sur le nombre de requêtes de récupération d'adresses IP réalisées à l'heure.

Chaque fichier partagé sur BitTorrent est référencé par un hash SHA1 qui l'identifie de manière unique. Ce hash est appelé infohash, et est fourni par les utilisateurs souhaitant partager un fichier. Pour partager un fichier, le logiciel BitTorrent (ou tout autre client BitTorrent) effectue une requête HTTP sur une URL d'annonce, en passant en paramètres différentes informations, telles que recensées sur le site contenant les spécifications du protocole:

<quote> * info_hash: urlencoded 20-byte SHA1 hash of the value of the info key from the Metainfo file. Note that the value will be a bencoded dictionary, given the definition of the info key above. * peer_id: urlencoded 20-byte string used as a unique ID for the client, generated by the client at startup. This is allowed to be any value, and may be binary data. There are currently no guidelines for generating this peer ID. However, one may rightly presume that it must at least be unique for your local machine, thus should probably incorporate things like process ID and perhaps a timestamp recorded at startup. See peer_id below for common client encodings of this field. * port: The port number that the client is listening on. Ports reserved for BitTorrent are typically 6881-6889. Clients may choose to give up if it cannot establish a port within this range. * uploaded: The total amount uploaded (since the client sent the 'started' event to the tracker) in base ten ASCII. While not explicitly stated in the official specification, the concensus is that this should be the total number of bytes uploaded. downloaded: The total amount downloaded (since the client sent the 'started' event to the tracker) in base ten ASCII. While not explicitly stated in the official specification, the consensus is that this should be the total number of bytes downloaded. * left: The number of bytes this client still has to download in base ten ASCII. compact: Setting this to 1 indicates that the client accepts a compact response. The announce-list is replaced by a peers string with 6 bytes per peer. The first four bytes are the host (in network byte order), the last two bytes are the port (again in network byte order). It should be noted that some trackers only support compact responses (for saving bandwidth) and either refuse requests without "compact=1" or simply send a compact response unless the request contains "compact=0" (in which case they will refuse the request.) [...] * ip: Optional. The true IP address of the client machine, in dotted quad format or rfc3513 defined hexed IPv6 address. Notes: In general this parameter is not necessary as the address of the client can be determined from the IP address from which the HTTP request came. The parameter is only needed in the case where the IP address that the request came in on is not the IP address of the client. This happens if the client is communicating to the tracker through a proxy (or a transparent web proxy/cache.) It also is necessary when both the client and the tracker are on the same local side of a NAT gateway. The reason for this is that otherwise the tracker would give out the internal (RFC1918) address of the client, which is not routable. Therefore the client must explicitly state its (external, routable) IP address to be given out to external peers. Various trackers treat this parameter differently. Some only honor it only if the IP address that the request came in on is in RFC1918 space. Others honor it unconditionally, while others ignore it completely. In case of IPv6 address (e.g.: 2001:db8:1:2::100) it indicates only that client can communicate via IPv6. * numwant: Optional. Number of peers that the client would like to receive from the tracker. This value is permitted to be zero. If omitted, typically defaults to 50 peers. </quote>

Le champ ip est un champ optionnel, mais le tracker peut le prendre en compte en lieu et place de l'adresse IP réelle effectuant la requête. Cependant la plupart des implémentations de trackers supportent ce paramètre sans le prendre en compte. Il est donc possible de spécifier une adresse IP autre que celle qui permet d'envoyer la requête, et donc potentiellement d'injecter des adresses IP falsifiées dans la liste des utilisateurs connectés, en envoyant une requête d'annonce avec une adresse IP falsifiée. Attention toutefois, certains trackers sont quand même sensibles à ce type d'injection.

La méthode TMG/YouHaveDownloaded

Un système de flicage de clients BitTorrent ne peut donc pas se fier qu'aux adresses IP fournies par le serveur central, le tracker, mais doit effectuer une vérification pro-active. Cette vérification est notamment faite via une connection aux adresses IP soupçonnées, et une requête permettant de valider si la personne partage une portion du fichier incriminé. Si oui, son adresse IP est répertoriée, et c'est a priori sur ce principe qu'est basée la détection de TMG.

YouHaveDownloaded utilise certainement le même type de détection, mais sans faire de vérification directe car il s'agit là d'une méthode lourde et fastidieuse. Du moins, c'est ce que l'on peut déduire des informations retournées par ce site, et notamment celles qui ont fait du bruit récemment: environ 250 adresses IP publiques appartenant à une plage d'adresses attribuée au Ministère de la Culture ont servi à télécharger des fichiers divers. Deux explications possibles:

Le problème de la méthode YouHaveDownloaded, c'est que les trackers servant de référence ne sont pas documentés sur le site, et il suffit qu'un seul de ces trackers soit vulnérable à une injection d'adresse IP pour que la totalité de la base de données créée à partir de ce tracker ne soit plus crédible. A mon humble avis, cette possibilité est très fortement probable, ce qui expliquerait les cas similaires observés de par le monde, aux USA et au Royaume-Uni notamment. Lorsque l'on sait que des hackers ont lancé il y a de cela presque deux ans des séries d'offensives contre l'HADOPI lors des discussions parlementaires et du vote, et fait la promotion d'un outil nommé seedfuck visant à injecter des adresses IP du Ministère de la Culture (mais pas que) dans des trackers de renom, il semble probable que ces mêmes personnes en veuille toujours au système mis en place.

MAJ du jeudi 29 décembre 2012: Un lecteur m'a signalé aussi le fait que les propriétaires de trackers (`et tout spécialement ceux de ThePirateBay <http://www.numerama.com/magazine/10885-the-pirate-bay-rend-la-riposte-graduee-dangereuse-pour-tous.html>`_) avaient parlé il y a de cela un moment d'injecter eux-mêmes des adresses IP falsifiées dans leurs trackers, ce qui est tout à fait probable mais peut aboutir à une baisse des performances du tracker si cette injection est trop présente (tout comme seedfuck abaisse les performances du tracker pour le fichier qu'il empoisonne).

BitTorrent, tu peux pas test.

Afin de vérifier cela, j'ai développé un petit outil (très rapidement, c'est-à-dire en commentant peu et pas très proprement, oui j'ai honte) permettant de tester l'injection d'adresse IP sur un tracker à partir de son URL d'annonce. Vous trouverez des URLs d'annonce sur ce site par exemple, afin de tester l'outil. Ce bout de code Python annonce un fichier inexistant (donc considéré comme nouveau) en précisant un paramètre ip, et vérifie que celui-ci est bien fourni ensuite par le serveur aux autres utilisateurs. L'outil seedfuck ne faisait pas cette vérification, et ne fonctionnait donc pas sur la grande majorité des trackers.

Je me suis servi de cet outil sur la plupart des "gros" trackers BitTorrent, et il s'avère que ceux-ci ne sont pas vulnérables. Néanmoins, je mets cet outil à disposition, cela pourrait servir ...

28
nov.
'11

Désactiver à froid AdMob grâce à Apktool et un poil de python

Publié le 28 novembre 2011

Je suis plutôt occupé sur pas mal de projets ces dernières semaines (dont un qui je l'espère sera publié avant noël ...) et je n'ai pas franchement eu le temps de rédiger quoique ce soit qui a un tant soit peu d'intérêt. Jusqu'à aujourd'hui, où je me suis dit que cela commençait à bien faire, et qu'il fallait que je me sorte les doigts du ... enfin que je me motive pour publier un truc sympa. J'avoue aussi que je suis malade à crever (comme tous les mecs malades), et que cela m'occupe un brin dans cette soirée hivernale.

Le modèle économique d'Android

Google promeut via Android la philosophie du Libre en diffusant le code source de son système d'exploitation mobile, contrairement à Apple et son IOS. Le problème, c'est que le Libre est bien (trop ?) souvent associé à la notion de gratuité, et les premiers geeks ayant opté pour le système Android s'attendaient de fait à avoir un panel d'applications gratuites intéressant et complet. Force est de remarquer que les développeurs d'applications n'avaient pas trop à y gagner, contrairement à l'iPhone où le prix du terminal détermine assez facilement le type d'acheteur/utilisateur/consommateur. Type qui ne se refuse pas de payer une application lorsqu'il en a besoin, contrairement à l'utilisateur Android.

Il est donc apparu comme essentiel, pour les développeurs d'applications mobiles destinées à Android, que la conception et la promotion d'une application se devait de passer par l'ancien modèle des sharewares: une version lite gratuite avec pub et dans certains cas des limitations, et une version complète payante cette fois-ci, qui supprime la pub et offre des fonctionnalités avancées. Regardez par exemple l'application Winamp.

Et bien sûr, nous simples utilisateurs d'Android, on doit subir les bandeaux de pub et autres joyeusetés qui nous prennent pour des vaches à lait. Sans parler des applications ne nécessitant a priori aucune connection de données qui de fait se mettent à pomper de la bande passante, faute à la pub. Et j'oublie encore le tracking des utilisateurs effectué par ces régies de pub ... Bref, il est des applications où l'on se dit que cela suffit, si on pouvait enlever cette satané pub cela nous ferait économiser de la bande passante et de précieuses minutes.

AdMob est le système de publicité (de monétisation d'application que l'on dit) géré par Google et qui s'intègre avec bon nombre d'applications présentes sur l'Android Market.

De la manière de désactiver AdView

Alors oui, AdMob c'est lourd, AdMob c'est pas bien. Et AdMon on va le désactiver, enfin pour les applications reposant sur le paquetage com.google.ads.AdView. Pour cela on se munit d'Apktool, notre super outil de désassemblage/réassemblage d'application Android, et on jette un oeil à une application qui utilise AdMob (tiens,* TaskManager* par exemple). Premier problème rencontré, l'outil proguard qui obfusque un brin les noms des classes et des méthodes. En fait, il s'agit d'un faux problème, car AdMon étant intégré à une application à l'aide d'une bibliothèque externe (un fichier .jar référencé dans le projet), les noms de classes et de méthodes sont restées intactes (ou presque). Pour être précis, certaines méthodes implémentées dans AdView sont exemptes d'obfuscation, car appelée par différents composants. J'ai pu identifier une méthode qui ne subit pas d'obfuscation:

.method public loadAd(Lcom/google/ads/AdRequest;)V
    .locals 1
    .parameter "adRequest"
    .prologue
    iget-object v0, p0, Lcom/google/ads/AdView;->a:Ld;
    invoke-virtual *v0*, Ld;->e()Landroid/app/Activity;
    move-result-object v0
    if-nez v0, :cond_0
    const-string v0, "activity was null while checking permissions."
    invoke-static *v0*, Lcom/google/ads/util/a;->e(Ljava/lang/String;)V
    :goto_0
    return-void
    :cond_0
    invoke-virtual *p0*, Lcom/google/ads/AdView;->isRefreshing()Z
    move-result v0
    if-eqz v0, :cond_1
    iget-object v0, p0, Lcom/google/ads/AdView;->a:Ld;
    invoke-virtual *v0*, Ld;->c()V
    :cond_1
    iget-object v0, p0, Lcom/google/ads/AdView;->a:Ld;
    invoke-virtual *v0, p1*, Ld;->a(Lcom/google/ads/AdRequest;)V
    goto :goto_0
.end method

Cette méthode est appelée lors du chargement de la publicité. Nous allons la court-circuiter en la patchant comme ceci:

.method public loadAd(Lcom/google/ads/AdRequest;)V
    .locals 1
    return-void
.end method

Et on reconstruit le paquetage Android (APK):

$ apktool b mobi.infolife.taskmanager-1 mobi.infolife.taskmanager-nopub.apk
$ jarsigner -v -keystore my.keystore myalias mobi.infolife.taskmanager-nopub.apk
$ adb mobi.infolife.taskmanager-nopub.apk

On peut ensuite admirer le résultat, une belle application sans publicité.

Bon allez zou, on automatise

Histoire de m'amuiser un peu, j'ai développé un petit script python qui automatise ce patch, libre à vous de l'utiliser dans un script bash ou je ne sais comment, c'est gratuit, cadeau, offert par la maison.

"""

This is a little python script to disable Google AdView from disassembled
Android's APKs (disassembled thanks to Apktool).

Pre-requesites:

    1. Install Apktool (http://code.google.com/p/android-apktool/)
    2. Be sure that jarsigner is installed on your system

How to use it:

    1. Disassemble your APK with Apktool

            $ apktool d com.example-1.apk

    2. Launch winston.py

            $ ./winston.py com.example-1

    3. Rebuild your APK

            $ apktool b com.example-1 com.example-noad.apk
            $ keytool -genkey -v -keystore my.keystore -alias myalias -keyalg RSA -keysize 2048 -validity 10000
            $ jarsigner -v -keystore my.keystore myalias com.example-noad.apk
            $ adb install com.example-noad.apk

And that's it !

Have fun ;)

"""

import re
import os
import optparse
from subprocess import Popen

class WinstonWolf:

    def __init__(self, directory):
            self.base_dir = directory

    def __checkGoogleAdView(self):
            return os.path.exists(os.path.join(self.base_dir,'smali/com/google/ads/AdView.smali'))

    def patchAdView(self):
            print '[i] Checking AdView presence ...'
            if self.__checkGoogleAdView():
                    try:
                            print '[i] Patching AdView file ...'
                            f = open(os.path.join(self.base_dir,'smali/com/google/ads/AdView.smali'),'r')
                            src = f.read()
                            start = src.index('.method public loadAd(Lcom/google/ads/AdRequest;)V')
                            patch = """
.method public loadAd(Lcom/google/ads/AdRequest;)V
    .locals 1
    return-void
.end method
                            """
                            if start:
                                    end = src`start:].index('.end method')
                                    if end:
                                            patched = src[:start] + patch + src[(start+end+11):]
                                            f.close()
                                            f = open(os.path.join(self.base_dir,'smali/com/google/ads/AdView.smali'),'w')
                                            f.write(patched)
                                            f.close()
                                            print '[i] AdView patched !'
                                            return True
                                    else:
                                            print '[!] Cannot patch: end method not found'
                                            return False
                            else:
                                    print '[!] Cannot find AdView <loadAd() method'
                                    return False

                    except IOError,e:
                            print '[!>`_ Unable to open/read/write file.'
                            return False
            else:
                    print '[!] AdView.smali not found !'

def createParser():
    parser = optparse.OptionParser("usage: %prog [options]")
    parser.add_option('-d','--directory',dest='directory',help='Directory of android application disassembly (created by apktool)',metavar='DIR')
    return parser

def banner():
    print '[ Android APK GoogleAds remover - By virtualabs (http://virtualabs.fr) ]'

if __name__ == '__main__':
    banner()
    (options, args) = createParser().parse_args()
    if options.directory:
            ww = WinstonWolf(options.directory)
            ww.patchAdView()

Conclusion

Je tiens tout de même à préciser qu'il existe pas mal de régies de pub qui supportent Android et qui utilisent chacune leur propre framework. On pourrait envisager d'améliorer ce petit bout de code de manière à gérer un grand nombre de bibliothèques de régies, ou tout simplement migrer sur une ROM custom qui permet d'éviter toute connexion à un serveur de publicité (de par la configuration du /etc/hosts). Bref, ce ne sont pas les solutions qui manquent, celle-ci a tout de même l'avantage de fonctionner sur n'importe quelle version d'Android. Et oui, je vais aller me soigner.

04
nov.
'11

Y'a bon les hashes MD5 du RIPE NCC !

Publié le 04 novembre 2011

Dans la série des choses bizarres que j'ai pu observer en furetant sur Internet, je vous présente l'authentification sécurisée de RIPE NCC. RIPE NCC, ce n'est rien d'autre que le centre de coordination des réseaux IP européens, autrement dit l'organisme en charge de la gestion des adresses IP sur le vieux continent. C'est cet organisme qui attribue des plages d'adresses IP aux sociétés, et qui leur offre le moyen de gérer leurs données. C'est aussi cet organisme qui est en charge du maintient des bases de données WHOIS pour les plages d'adresses IP dont il a la charge.

Authentification vous avez dit ?

Bien sûr, l'accès à ces données est protégée par une authentification, utile seulement pour l'ajout, la modification et la suppression d'enregistrements. Histoire que madame Michu ne puisse pas modifier les adresses de courriel associées à un mainteneur par exemple (très utilisées en cas d'abus, de phishing, ou que sais-je ...). D'ailleurs le RIPE NCC met à disposition une page explicitant les méthodes d'authentification supportées, ce qui inclut: * l'authentification par hash FreeBSD MD5 * l'authentification par certificat X.509 * l'authentification par clef PGP

Je ne vous cache pas que les deux dernières options sont mises en avant par le RIPE NCC, celui-ci précisant que l'authentification par hash FreeBSD MD5 est vulnérable à deux attaques: * le cassage de mot de passe (à partir du hash FreeBSD MD5) * l'interception du mot de passe (celui-ci étant notamment envoyé en clair dans les emails de mise-à-jour)

Hash MD5 vs PGP vs X.509

La question est donc posée: combien de mainteneurs emploient l'authentification par hash FreeBSD MD5 ? Pour répondre à cette question, rien ne vaut un bon vieux google dork:

site:apps.db.ripe.net "mntner: " "MD5-PW" ##NOMDESOCIETE##

Ce qui nous donne de beaux résultats, comme par exemple avec Microsoft:

Hé oui, l'authentification MD5 est présente, et seulement celle-là ! C'est aussi le cas de DEXIA, de BNP Paribas, d'ORACLE, et j'en passe. Le nombre de ces champs d'authentification n'est pas limité, il est donc possible de paramétrer plusieurs mots de passe pour un seul objet, ce qui rend la probabilité d'attaque plus grande. Avec le hack google présenté ci-dessus, on trouve énormément de mainteneurs qui utilisent ce type d'authentification, exposant de fait leurs données.

Il est d'ailleurs étrange de voir que le site de RIPE NCC propose à la consultation le contenu de ces champs d'authentification, qui ne sont absolument pas nécessaires au quidam. Peut-être que les cacher pourrait empêcher de les casser facilement ? Dire que des groupes de hackers exploitent des injections SQL en masse pour obtenir ces précieux sésames ... Alors que le RIPE NCC les donne publiquement ! Update: Ils ont publié depuis une note sur le site des labs du RIPE NCC concernant le futur de ces hashes MD5, cf [1].

Dans certains cas, l'authentification MD5 est couplée à une authentification par certificat ou clef PGP, comme c'est le cas par exemple pour le mainteneur RAIN-TRANSPAC:

Mais gros problème pour ce mainteneur, le mot de passe correspondant à un de ses hashes MD5 n'est absolument pas solide (7 caractères alphabétiques dont 1 majuscule, merci Sorcier_FXK) et peut autoriser la modification des objets dépendant de ce mainteneur (qui je le rappelle, correspond aux Réseau d'Accès à l'INternet, appartenant à France Telecom/Orange Business). Une fois le mot de passe retrouvé, il est aisé de modifier le contenu des enregistrements via le formulaire web dédié.

Quelques statistiques

Suite à cela, j'ai codé un petit outil qui se base sur un ancien top 100 des mainteneurs, afin de déterminer le pourcentage de ces mainteneurs utilisant les différents modes d'authentification proposés, et les résultats sont les suivants (sur 85 mainteneurs encore en activité):

Il s'agit de statistiques calculées assez rapidement, mais cela montre tout de même la prépondérance de l'authentification par hash FreeBSD MD5, et donc la vulnérabilité potentielle de ces objets mainteneurs.

Impact & responsabilités

Le site du RIPE NCC précise bien que l'authentification via hash FreeBSD MD5 est plus faible que l'authentification via PGP ou certificat X.509, seulement la grande majorité des utilisateurs historiques ont de prime abord opté pour cette méthode d'authentification, fragilisant de fait l'intégrité des données stockées dans la base de données du RIPE.

Il est donc de la responsabilité des mainteneurs d'éviter à tout prix l'emploi de ce mode d'authentification, et de lui préférer PGP ou l'emploi de certificats X.509. Quant au RIPE NCC, il est de leur responsabilité de ne pas diffuser les hashes FreeBSD MD5 afin de ne pas fragiliser l'intégrité des données de leurs utilisateurs.

EDIT: J'ai été contacté après coup par Stephane Bortzmeyer et Alix Guillard, le premier m'ayant donné de plus amples informations sur les ressources offertes par le RIPE NCC et le second m'ayant dirigé vers la mailing-list db-wg (database working group) du RIPE afin d'exposer mon point de vue. Un thread a été démarré, auquel j'ai apporté mon soutien: http://www.ripe.net/ripe/mail/archives/db-wg/2011-November/001993.html. En espérant que cela puisse permettre de faire avancer les choses. Merci à vous deux.

EDIT #2: Le RIPE NCC a publié le 09/11/2011 un billet sur le site des labs RIPE NCC explicitant le problème et envisageant de prendre les mesures adéquates, comme discuté sur la mailing-list db-wg.

[1] Le billet des labs RIPE NCC concernant l'authentification par hash FreeBSD MD5



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.