Ca faisait bien presque neuf mois que Mme virtu était enceinte, presque neuf mois de préparation, et à chaque fois je me disais que j'avais le temps, que ça arriverait plus tard. On avait fait pas mal d'aller-retours à la maternité, tout plein d'examens et un suivi complet, franchement c'était impeccable. J'ai aussi appris à hacker le système de monitoring, car bon, je n'ai pas pu m'en empêcher.
Kezako le monitoring ?
| alt: |
|---|
Selon le placement du capteur destiné à écouter les battements du cœur de bébé, l'appareil détecte plus ou moins bien et lève une alerte quand il ne détecte plus de battements. Cela peut être dû à des mouvements de bébé, ou à une mauvaise fixation du capteur. Dans mon cas, bébé bougeait pas mal et donc au bout de dix minutes on a eu droit au désagréables "bips" de la machine. Obligé d'appeler une sage-femme, qui appuie sur deux boutons et règle ça en deux temps trois mouvements. Mais cela a repris cinq minutes après, ce qui commençait à m'agacer.
Hacking de monitoring, à la main siouplait
Le gros avantage de cet appareil, c'est qu'il possède de bons haut-parleurs qui diffusent le son des battements du cœur de bébé, et donc que j'ai pu bien entendre le rythme de ce petit cœur (entre 135 et 145 bpm pour un petit bout dans le ventre de sa maman). Pour rappel, les ventricules du cœur subissent une systole (scindée en trois temps distincts) et une diastole, ce qui provoque ce rythme très particulier qu'est celui cœur. Pour plus d'information, Wikipedia pourra vous renseigner mieux que moi.
Pour m'amuser (car oui, pendant le monitoring on s'ennuie à mourir, même avec un smartphone dans les mains), j'ai essayé de reproduire à l'oreille le même rythme, en tapotant avec deux doigts sur le ventre de la maman et en essayant de retrouver le rythme des battements cardiaques. Le système étant basé que sur un capteur de son, il est facile de le tromper. J'y étais presque arrivé, le rythme s'étant de nouveau affiché à l'écran, mais mes premières tentatives n'ont pas forcément été fructueuses: le système détectait un battement de 77bpm, ce qui est loin de la normale. Et donc les alarmes ont recommencé (diable !). Ceci dit, je suis sûr qu'il y a moyen de reproduire fidèlement le bon battement avec un bon coup de main et un brin d'oreille musicale (et de sens du rythme). Pour le coup, ça a agacé la future maman qui n'a pas pu s'enpêcher de dire qu'il fallait que je touche à tout. Ce qui n'était pas faux.
La nuit la plus longue
Après cette drôle tentative de hack de système médical, les choses se sont accélérées et la maman est entrée en salle de travail. C'en était parti pour une dizaine d'heures d'attente et d'angoisse, à la rassurer et à essayer de dormir un peu. A ce moment, je me disais encore que j'avais un peu de répit, jusqu'à ce que la sage-femme entre en trombe dans la pièce en disant que c'était le moment, qu'il fallait y aller. Et le petit bout arriva. Quoiqu'on en dise, c'est un des rares moments où l'on sent que l'on ne maîtrise rien, mais la magie opère. La petite geekette était là, déjà toute curieuse avec ses yeux presque ouverts.
De longues heures d'attente pour un résultat somme toute époustouflant. On ne sait pas vraiment ce que c'est avant que ça arrive, et c'est çe qui fait en partie la magie du moment.
L'après
Comme vous vous en doutez, cette naissance aura un impact sur les écrits de ce site, et d'autant plus sur leur fréquence. Cela a déjà commencé avec ce post qui s'éloigne un peu du hack (quoique ...), mais bon, cela doit être du au fait qu'en tant que nouveau père je suis fier de ma geekette, et souhaite le dire au monde entier. Il faut avouer tout demême qu'Internet est un super moyen pour cela ! Promis, je vous épargnerai les posts personnels par la suite.
La philosophie du hacking implique une curiosité constante, et donc le fait que l'on mette notre nez un peu partout. C'est grâce à cela que des problèmes voire même des vulnérabilités sont identifiés, et résolus. Le problème, car il y a problème, réside dans la difficile communication et le fait que l'on fasse une très bonne cible en faisant part d'une trouvaille.
La curiosité est un vilain défaut
Tout le monde le sait. C'est pas bien. Mais il est des personnes qui ne peuvent s'empêcher de se poser des questions, légitimes ou non. Ce n'est pas (encore) une maladie, mais c'est ce qui motive certaines personnes. Dont moi. Et je dois avouer que la plupart du temps, c'est assez payant. Cette curiosité est le moteur essentiel de ce blog, de mes outils, etc ... Tout vient de questions que n'importe qui (enfin, je suppose que n'importe qui se les pose) pourrait se poser, et de la réflexion qui s'ensuit.
Malheureusement (sic), cette curiosité pousse des fois à tester ou vérifier des faits, à la limite de la légalité. Ce qui permet de cerner correctement un problème et de le reporter ou d'alerter. Et c'est lorsqu'on alerte, que l'on prévient, que les choses se corsent. Car dans certains cas de figure, la question d'où part une analyse ou une réflexion peut être considérée comme inopportune voire comme une action offensive. Alors qu'il s'agissait de pure curiosité, sans animosité aucune. Mais certaines personnes ne voient pas cela d'un bon œil, car le fait d'alerter ou de reporter peut nuire à leur image (ou celle de leur entreprise, ou administration) et je comprends tout à fait le besoin de se couvrir. Il est vrai qu'en France on a cette aversion naturelle contre les hackers (favorisée par les médias), et d'autant plus pour ceux qui publient des choses qui fâchent ou ennuient.
La nécessité de transparence ?
Il est évident que la publication, la diffusion, le fait d'alerter sur une vulnérabilité ou un problème identifié ne doit pas être fait n'importe comment. Certains préfèrent l'anonymat, et dévoilent à coup de pastebin des fuites d'informations, ou des vulnérabilités originales. S'ensuit généralement une tentative d'identification de la source, qui des fois aboutit après de fastidieuses recherches. Mais le fait de publier sous couvert d'anonymat vous place directement dans la mauvaise catégorie: pourquoi vous cachez-vous, si vous n'avez rien à vous reprocher ?
A contrario, beaucoup d'entre-nous pensent que la transparence est le meilleur atout. Nous nous permettons de publier des alertes, de reporter des problèmes ou des erreurs en toute bonne foi, sans nous cacher forcément derrière un pseudonyme (bien qu'on puisse en avoir un) et revendiquer la trouvaille. Si jamais quelqu'un est froissé, ou ennuyé par une publication, l'auteur de celle-ci est identifiable et joignable. De fait, annoncer sur Twitter ou par email sans masquer son identité est gage de sérieux, ou de grande folie. Malheureusement, lorsque les choses se gâtent, la seconde option est préférée.
Heuu ... à quand une prise de conscience ?
De suite, on tape sur la personne qui a identifié le problème. Vous annoncez sur Twitter que vous avez identifié une vulnérabilité dans un produit sans donner trop de détails ? Vous dévoilez un fichier planqué au fin fond du web et contenant des informations critiques ? Vous avez identifié un problème ou une anomalie et communiquez dessus au monde entier ? You're doing it wrong. Et même si l'envie vous prend de le transmettre par email, l'issue est globalement la même: l'annonceur est fautif. Victime de sa curiosité. Et peut-être de sa volonté d'alerter et de faire connaître sa trouvaille. On tire sur le messager, meme si je dois avouer que dans certains cas il n'est pas forcément tout blanc.
La problématique est ancienne: comment dévoiler une vulnérabilité/anomalie/faille en toute bonne foi, sans se cacher, et sans risquer les foudres des personnes concernées ? Quid du responsible disclosure ? C'est un débat qui dure, bien qu'en France il soit déjà plié, j'en ai bien peur. Par expérience, j'ai tendance à dire que lorsque l'on tente de dévoiler ou d'alerter, on devient une cible (que cela soit justifié ou non, là n'est pas la question). Certes, nous pronons le hacking "éthique", et cette volonté de transparence et de communication fait partie de cette éthique. Mais sincèrement, il est plus risqué de communiquer sur une vulnérabilité que de se taire et de la garder pour soi. Beaucoup en ont fait l'expérience: Guillermito [1], Damien Bancal [2] par exemple.
Alors, comment dévoiler/communiquer ?
Le constat est triste: soit on dévoile et on risque les sanctions prévues aux différents articles de la loi française (pour rappel, les articles 323-1 à 323-7 [3], avec des peines allant de 2 ans d'emprisonnement et 30 000€ d'amende à 5 ans d'emprisonnement et 75 000€ d'amende), bien que de bonne foi, soit on se tait et les choses ne bougent pas. J'avoue que cette vision est pessimiste, mais sincèrement je ne vois pas comment on pourrait éviter cela. Triste constat, disais-je.
Allons-nous devoir attendre que les responsables sécurité arrêtent de faire la sourde oreille et tentent de sauver leur place sans assumer les problèmes qui se posent ? Faut-il encore diaboliser Internet, l'informatique et le hacking en général, ainsi que les personnes compétentes en France ? L'ANSSI a ouvert la voie (du moins on ose le croire), en faisant appel à la culture hacker (il n'y a qu'à voir leur wallpaper `4]), en recrutant massivement et en le faisant savoir à différentes conférences sécurité prisées des hackers (non, pas de troll sur l'AN^W^WSSTIC). A qui le tour ?
Références
[1] [L'affaire Guillermito <http://guillermito2.net/archives/2004_12_28.html>`_
`2] [Damien Bancal (Zataz) vs le FTP anonyme <http://www.pcinpact.com/news/48753-zataz-faille-securite-trou-signalement.htm>`_
`3] [Code pénal, Livre III, Titre III, Chapitre III: Des atteintes aux systèmes de traitement automatisés de données <http://www.legifrance.gouv.fr/affichCode.do;jsessionid=DBA449F582FD48DF11068D44A409B79B.tpdjo07v_3?idSectionTA=LEGISCTA000006149839&cidTexte=LEGITEXT000006070719&dateTexte=20120510>`_
`4] [Wallpaper ANSSI <http://www.ssi.gouv.fr/IMG/png/wallpaper-anssi-2560x1920.png>`_
Certainement que, comme moi un de ces jours où l'on a le cerveau patraque, vous vous êtes retrouvés face à une vulnérabilité permettant une inclusion de fichier local sans avoir identifié de moyen d'envoyer (ou de créer) sur le serveur distant un fichier contenant du code actif... Et ce n'est pas faute d'avoir essayé d'injecter dans le fichier de session, voire même dans les logs ou les images uploadées via un formulaire ! Mais rien n'y fait. Même ce satané formulaire d'upload écrit en PHP et utilisant la bibliothèque GD pour décoder et écrire ensuite sur disque les images qui lui sont envoyées nous empêche d'obtenir un remote shell. Damned, we're doomed.
Quelques fuites d'informations
C'est vrai que d'habitude, l'envoi d'une image au format JPEG par exemple avec un commentaire paramétré avec Gimp fait l'affaire, en dernier recours. Seulement dans mon cas, ce n'était pas possible, la bibliothèque GD étant utilisée pour décoder et écrire ensuite le fichier image sur disque: celle-ci a la fâcheuse manie de remplacer tout commentaire existant et d'y coller le sien:
Pour le coup, cette particularité est très intéressante: la qualité utilisée pour stocker l'image est dévoilée, en l'occurrence il s'agit d'une qualité de 99 (celle par défaut étant de 75 environ). On sait aussi que l'image a bien été générée par la bibliothèque GD. Bon, c'est pas folichon, mais ca peut aider.
Découverte du format de fichier JPEG
Le site distant n'acceptant que les images au format JPEG, j'ai donc décidé de mettre les mains dans le cambouis, et de voir comment est structuré ce format de fichier. L'objectif principal étant d'arriver à trouver une zone dans laquelle on peut écrire mais qui n'est pas supprimée par cette satanée bibliothèque. Je suis donc parti à la chasse aux sources, et j'en ai trouvé plusieurs:
Le format de fichier JPEG (Joint Photographic Expert Group) est un format de stockage d'image qui emploie une compression avec perte, basée sur une conversion d'une disposition de pixels en répartition fréquentielle (grosso modo). Le problème de ce format de fichier, c'est que la configuration des pixels composant l'image n'est pas écrite telle quelle dans le fichier, contrairement aux images dites raster (comme le format BMP de Microsoft, ou le format GIF), et cela pose des soucis. En particulier quand on cherche à insérer un bout de code PHP pour exploiter convenablement une LFI.
De plus, le format de fichier est assez particulier, l'ensemble des informations étant réparties dans des sections, définies par des marqueurs. Un marqueur débute toujours par l'octet de valeur 255 (0xFF), et contient un code indiquant son rôle. Ainsi, le marqueur ayant pour code 0xD8 marque le début de l'image (Start of Image, ou SOI), et celui ayant pour code 0xD9 la fin de l'image (EOI). D'autres marqueurs sont aussi définis, dont celui définissant un commentaire, 0xFE. Je vous renvoie aux quelques liens donnés précédemment pour de plus amples informations.
Mais alors, où écrire dans ce format de fichier ?
C'est toute la question. Il ne peut y avoir qu'un champ de commentaire dans un fichier JPEG, et de toute façon nous avons vu que celui-ci était écrasé par la bibliothèque GD lors de la sauvegarde. Le format de fichier JPEG autorise aussi des marqueurs spécifiques aux applications (les fameux APPX), mais encore une fois ceux-ci ne sont pas pris en compte par GD. Il ne nous reste pas d'autre choix que de tenter une insertion dans les données stockées dans le fichier, servant à la reconstruction de l'image.
En théorie, il suffit de localiser la section SOS (Start of Scan, ayant pour marqueur 0xDA), de trouver les données compressées qui la suivent et de remplacer les premiers octets avec notre payload PHP. Oui, en théorie c'est censé fonctionner. Seulement en pratique, notre payload PHP va être interprété comme une donnée compressée et servir ensuite à générer une image composée de pixels que nous ne maîtrisons pas. Une fois recompressée par la bibliothèque GD, rien ne nous garantit que notre payload PHP sera conservé. L'insertion idéale consisterait à injecter à la place des données permettant de reconstituer les pixels notre payload PHP, et lorsque GD décode puis encode l'image, que notre payload soit conservé et écrit sur disque. Dans le cas où le fichier ne transite pas par la bibliothèque GD, celui-ci contient tout de même notre payload et fonctionnera. Ainsi, nous serions à même de construire une image Jpeg contenant du code PHP malveillant, et résistante aux transformations induites par la compression réalisée par la bibliothèque GD !
Création d'une image Jpeg "bulletproof"
Pour pouvoir créer ces merveilleuses images, j'ai tout d'abord codé en Python le code réalisant l'injection. Pour effectuer celle-ci, il suffit de rechercher la séquence d'octet 0xFF 0xDA (correspondant à la section Start of Scan), puis de lire les deux octets qui suivent (contenant la taille de la section stockée sur 2 octets en big-endian), afin de trouver l'endroit où les données compressées sont écrites. On recherche ensuite à partir de cet emplacement le marqueur de fin d'image (0xFF 0xD9), et les données situées entre les deux correspondent aux données compressées définissant le contenu de l'image (enfin, une partie pour être précis, mais là n'est pas la question).
Il est ensuite trivial de remplacer les quelques octets de début par notre payload. Notez que dans le code présent j'ai prévu un décalage variable, j'y reviendrai plus tard. Voici le code de cette fonction:
def insertPayload(_in, _out, payload,off):
img = _in
# look for 'FF DA' (SOS)
sos = img.index("\xFF\xDA")
sos_size = struct.unpack('>H',img[sos+2:sos+4])[0]
sod = sos_size+2
# look for 'FF D9' (EOI)
eoi = img[sod:].index("\xFF\xD9")
# enough size ?
if (eoi - sod - off)>=len(payload):
_out.write(img[:sod+sos+off]+payload+img[sod+sos+len(payload)+off:])
return True
else:
return False
Pour tester les images générées, j'ai installé les bindings Python de la bibliothèque GD, sous debian le package nommé python-gd. Ces bindings permettent de simuler à l'aide de Python l'ensemble des traitements effectués par la bibliothèque GD, et en particulier de reproduire ce qu'il se passe sur le serveur cible à savoir l'ouverture puis l'écriture sur disque de l'image uploadée.
Un point est cependant capital à prendre en compte: la qualité de l'image. GD permet de définir une qualité (tout comme plein d'autres logiciels d'infographie, comme GIMP par exemple) afin d'ajuster la taille du fichier sur disque. Plus la qualité est bonne (proche ou égale à 100), plus le fichier sera gros et l'image nette, et a contrario plus celle-ci est faible plus le fichier sera petit et l'image dégradée. Ce facteur de qualité est très important: il faut utiliser exactement le même facteur lors de la génération de l'image bulletproof afin d'être sûr que le serveur distant va bien générer notre payload lors de l'écriture sur disque. La fuite d'information découverte précédemment va sûrement vous être utile afin de déterminer la qualité employée =).
J'ai automatisé la génération des images bulletproof à l'aide d'un script Python (encore un), et j'ai ainsi pu générer des images contenant le code PHP (ou équivalent) suivant pour les facteurs de qualité de 52 à 98:
<?php system($_GET['c']); ?>
Certes, quelques variantes ont du être employées pour assurer une insertion maximale, mais le résultat est plutôt intéressant. De plus, mon script essaie d'insérer le payload à différents endroits, pas forcément au début de la section (vous vous rappelez de l'offset dans la fonction d'insertion ?), car les phases de décompression/compression rendent le résultat un poil aléatoire. Ainsi, voici à quoi ressemble une image avant traitement par la bibliothèque GD (côté serveur), image contenant notre payload PHP (pour la qualité par défaut):
En regardant en détail le contenu de l'image, on peut apercevoir le payload PHP:
Fin mot de l'histoire
Avec cette image JPEG, j'ai pu facilement contourner la restriction imposée via la bibliothèque GD et forcer celle-ci à écrire elle-même une image contenant un code PHP malveillant, qui m'a permis d'exécuter des commandes systèmes sur le serveur distant à l'aide de la vulnérabilité d'inclusion de fichier local trouvée auparavant.
Pour vous éviter de générer tout vous-même, je vous ai préparé une petite archive contenant mon code python ayant servi à la génération de toutes les images bulletproof, ainsi que les images elles-même (32x32 pixels). Si avec ça je ne vous gâte pas pour Pâques, je ne comprends pas ...
Pour terminer sur une note sécuritaire, lorsque vous autorisez l'upload d'images au format JPEG (mais ceci est d'ailleurs vrai avec d'autres formats comme BMP ou PNG, une attaque identique pouvant être réalisée) prenez plusieurs précautions:
version de GD employée: 2.0.36rc1
version de Python employée: 2.7