Simple Art of Steganography

J’ai reçu il y a quelques jours de Vincent le mail ci-dessous.

----------  Message transmis  ----------
Sujet : Simple Art of Steganography
Date : jeudi 21 octobre 2010
De : ***
À : ***

(1) Download and open "hug me.jpg" , it is a simple jpg picture, representing
my Teddy bear hugging ...;  rename the extension of this file to MP3, so you'll
have "hug me.mp3" , you used to think that it wont work if you simply change
the extension of a file? well you were wrong ! try it, open it [ i used
WINAMP].

can u hear it? can you hear the picture file?!  again change the file extension
to JPG and it is still a pictre file .

(2) Download and open "veronamania.jpg",  change the file extension to RAR, so
you will have "veronamania.rar",  use WinRAR [or other RAR handlers]  to
extract this file and see the other JPG file inside !

well it is called "steganography", refere to link below for complete
definition:

http://www.garykessler.net/library/steganography.html

http://en.wikipedia.org/wiki/Steganography

-------------------------------------------------------

Je vais donc lui (par la même occasion, vous) répondre. Donc je pense qu’il n’y a rien de bien compliqué. Il s’agit de la technique qui consiste à concaténer un fichier (image, musique, etc.) à une archive. Test:

$ zip test.zip test.txt $ cat Stop_And_Stare.ogg test.zip > Stop_And_Stare-1.ogg

Vous pourrez toujours écouter votre musique. En renommant l’extension vous retrouverez le fichier texte. En gros, c’est du au fait que votre logiciel se contente d’interpréter les données qu’il attend (tar/zip décodent les données compressées). Je préfère de loin les méthodes évoquées précédemment sur ce blog. Elles sont plus marrantes et n’agrandissent pas la taille du fichier.

§

Tout autre chose, les archives de pyAggr3g470r présentent maintenant mieux les articles. Évidemment on peut toujours exporter en fichier brut.

Le MacBook va disparaître

D’un côté c’est une très bonne nouvelle. D’un autre il sera remplacé en quelque sortes par l’iPad avec le même principe de Market que l’iPhone.

Tout ce qui se passe en ce moment me fait penser à ces anciens (excellents) billets [Tinkerer’s Sunset, In pursuit of root]. D’ici peu le compte administrateur n’existera plus sur les ordinateurs Mac. Je sentais cela arriver depuis un moment (on peut comparer par exemple la méthode pour installer une application non présente sur le Market entre Android et l’iPhone).

Aujourd’hui, je suis convaincu de la vérité de ces propos (même si pour le moment Apple ne bloquera pas d’autres sources que le Market pour le MacBook, pour le moment). Que pourra-t-on encore faire avec ce genre d’ordinateur?

Le hacker sous Mac va donc disparaître. Vos enfants n’auront plus le plaisir de démonter la machine familiale ;-) En tout cas pas avec un produit Apple. Nous devons être une des dernières générations à avoir eu cette chance

Ce qui me chiffone. Comment peut vouloir un iPad? Et pour faire quoi avec? Et surtout c’est quoi de nos jours un MacBook (de même qu’un iPad)?

Pour ne pas faire un billet trop pessimiste, il faut quand même dire qu’il est toujours possible d’installer des logiciels à partir des sources.

MAS (SMA) et MapReduce

Ces derniers temps je m’intéresse un peu à MapReduce (les personnes ayant le bon goût d’apprécier OCaml connaissent parfaitement le principe;-) ). Lisez ce papier de Google par exemple.

Vous trouverez de nombreuses implémentations. En voici une en Python, mincemeat. Regardez le petit exemple. C’est simple et ça fonctionne remarquablement bien.

Voilà. Donc mincemeat ma donné envie d’utiliser le principe de MapReduce avec la programmation multi-agent. Pour le moment je cherche une manière propre et originale de faire cela. Mais je me demande surtout quel sera l’intérêt réel, puisque mincemint (par exemple) fait cela très bien déjà.

Qu’en pensez vous? Dois-je marier SPADE et le principe MapReduce? J’ai peur de développer quelque chose d’inutilement compliqué (mincemeat c’est 400 lignes de code) et pas forcément plus utile que mincemint.

Je pense que l’avantage en faisant ça proprement serait d’avoir à la fin quelque chose de pus “souple”, plus “puissant” et plus facilement adaptable à différents problèmes. Ça vaut peut être le coup d’essayer…

Et si vous avez des idées/conseils…

pyAggr3g470r – Archives

Voilà un exemple d’archives au format HTML qu’il est possible de générer automatiquement avec pyAggr3g470r. Vous pouvez regarder ce billet, celui-ci ou celui d’un de mes lecteurs ;-) . Sympa non? En tout cas moi j’aime. Il est aussi possible d’exporter les articles au format texte brut. Cette archive contient 7029 articles. Je pense que je vais améliorer le rendu HTML pour que l’affichage soit identique à celui de pyAggr3g470r (texte justifié sur 50% de la page au centre) et ajouter un menu HTML pour naviguer dans l’archive. Ce que je trouve super c’est qu’on peut facilement archiver un tas d’informations provenant de billets souvent très instructifs et passionnants. En résumé 7029 articles pour 16803840 bytes au format MySQL, 17451828 bytes en HTML ou 9006880 bytes en texte brut.

Maintenant j’ai une sauvegarde en texte brut sur mon disque dur de mes blogs favoris. Et je peux facilement faire des recherches.

pyAggr3g470r 2.0 – News

pyAggr3g470r 2.0

Quelques nouvelles sur l’évolution de pyAggr3g470r:

  • historique: on peut maintenant naviguer dans l’ensemble des articles chronologiquement via des nuages de tags;
  • la documentation et le wiki sont à jour avec un script d’installation;
  • utilisation si disponible du module blist de haute performance pour les listes;
  • encore un peu plus social avec un compteur Twitter. On peut donc maintenant partager des articles avec Google Buzz, delicious, Identi.ca, Digg, reddit, Scoopeo, Blogmarks, Twitter et par QR Code.
  • nombreuses petites améliorations de l’interface (meilleur présentation de la description des articles pour une lecture plus agréable) et du code en général;
  • ma base de test contient maintenant plus de 6500 articles avec description et c’est toujours rapide.

Voilà, ce n’est pas grand chose. Ces dernières semaines les évolutions sont moins importantes que d’habitude. Il s’agit surtout d’améliorations. La base d’articles commence à être assez conséquente pour faire des tests. D’ici une semaine ou deux on verra plus de nouveautés, promis ;-)

WebM, VP8, Theora et Vorbis

L’objectif de ce billet est d’éclaircir un peu les choses à propos des nouveaux formats dont on parle de plus en plus. J’espère qu’il vous sera utile, il l’est pour moi. Let’s go.

WebM (.webm) est un conteneur vidéo très proche du format Matroska (.mkv) conteneur vidéo libre de haute qualité. WebM a été annoncé lors du Google I/O 2010. Un autre excellent conteneur très connu est Ogg (.ogv).

Ce qui est important c’est que WebM sera supporté nativement (sans plug-ins) par Chromium, Google Chrome, Mozilla Firefox et Opera. De plus, voici un billet d’Adobe annonçant que Flash supportera également WebM (maintenant pensez à Google TV. Pour le moment H.264 est conseillé en cas d’utilisation de Flash 10.1).

Dans un conteneur, on retrouve généralement des codecs, audio et vidéo. Parfois des sous-titres. WebM utilise pour la vidéo VP8 et Vorbis pour l’audio.

VP8

Pour expliquer ce qu’est le VP8 il faut revenir au codec VP3. VP3 est un format vidéo de compression à perte assez performant. Appartenant initialement à la société On2. En juin 2002, On2 a gracieusement cédé VP3 à Xiph.org Foundation. Le codec vidéo Theora est ainsi basé sur VP3. Theora peut être embarqué dans n’importe quel conteneur, vous le verrez souvent dans du Ogg. Firefox 3.5 supporte nativement Theora avec le conteneur Ogg. VP8 est un autre codec vidéo d’On2 plus évolué que VP3 et de qualité similaire à H.264. Le codec vidéo VP8 est plus évolué que Theora.

Depuis le 18 mai 2010 (19 heures 13 minutes 19 secondes précisément ;-)) les spécifications de VP8 sont disponibles ainsi qu’un exemple de décodeur/encodeur (l’annonce d’Adobe date du 19 mai 2010). Google a acquis ce format à coup de millions et a placé son code sous licence BSD. Merci Google (cependant il reste quelques brevets, héritage d’On2).

Vorbis

Vorbis est la partie audio de WebM. Il s’agit donc d’un codec. Souvent retrouvé dans des fichiers Ogg. (Personnellement, j’utilise exclusivement ce format pour ma musique depuis au moins trois ans. Et avec Android, c’est parfait. Il me manque un décodeur Vorbis dans la voiture…). Bref, ne dites plus jamais Ogg Vorbis, techniquement cela n’existe pas. Firefox 3.5 supporte nativement Vorbis avec le conteneur Ogg. Android également.

Les Linuxiens connaissent très bien le décodeur ffmpeg supportant Vorbis. À ce point on comprend qu’il va être possible de générer des fichiers WebM avec ffmpeg. ffmpeg est multi-plateformes et sous licence LGPG/GPL. C’est génial. Vorbis est à présent contenu dans Ogg, Matroska ou WebM.

Donc si comme moi vous utilisez depuis longtemps exclusivement le format Vorbis pour l’audio, il sera simple d’obtenir des fichiers audio WebM sans ré-encodage ni perte (ayant compris ce que vous venez de lire ci-dessus). Voir ce billet. Pour des fichiers vidéo WebM on peut ainsi simplement utiliser ffmpeg.

§

Comme ci ce n’était pas assez, voilà WebP (prononcez weppy !).
Google a annoncé WebP le 30 septembre 2010. Il s’agit d’une méthode de compression. Ce sont des images basées sur celles du VP8 (codec vidéo) dans un conteneur RIFF (vous connaissez bien les fichiers .wav). (Je ne sais pas si l’objectif de WebP est de concurrencer frontalement JPEG pour la photographie. Je n’ai pas l’impression. Je pense que WebP est vraiment là pour améliorer les connections, faciliter le chargement des pages pour les terminaux mobiles, Google Chrome OS et autres nouveaux petits appareils. Cependant WebP semble avoir une meilleur compression que JPEG, surtout pour les petites images. Les sites utilisent pratiquement que des petites images. Voici une intéressante galerie de comparaison WebP/JPEG. En tout cas si WebP devait concurrencer JPEG pour la photographie, je suis pour WebP maintenant que je comprends un peu mieux ce qu’il y a derrière).

Pour le moment le canal alpha (pour la transparence) n’est pas encore supporté. (Je pense que rien que pour bouger les choses il serait bien que WebP fasse un peu trembler le JPEG (ici le Joint Photographic Experts Group). De la même manière que WebM a fait un chouilla bouger H.264. Un chouilla.

§

Conseils de lecture pour approfondir:

Stéganô and the Free Software Song

hg clone http://bitbucket.org/cedricbonhomme/stegano/
cd stegano
wget http://www.gnu.org/music/free-software-song.ogg
./lsb-s.py --hide -i ./pictures/Montenach.png -o ./pictures/Montenach_enc.png -f ./free-software-song.ogg
rm free-software-song.ogg
./lsb-s.py --reveal -i ./pictures/Montenach_enc.png -b ./cool-song.ogg

Ceci seulement pour annoncer qu’une ligne de commande est disponible (avec d’autres améliorations). Plus d’informations sur le Wiki. Et n’hésitez pas à cacher cette fabuleuse chanson dans toutes vos images.

Encore plus vite

Ci-dessous une stéganalyse de cette photo.

Stéganalyse de l'image originale
Stéganalyse de l'image originale

Maintenant une stéganalyse du cadeau d’hier:

Stéganalyse de l'image contenant un message
Stéganalyse de l'image contenant un message

On constate deux choses:

  • il reste beaucoup de place. L’algorithme de stéganographie LSB utilise le dernier bit de chaque couleur des pixels. De nombreuses implémentations utilisent les deux derniers. Ce qui signifie que j’aurai pu utiliser une photo de seulement 5Mo pour faire la même chose.
  • des données encodées en base 64 sont facilement détectables par stéganalyse. Il faudrait ajouter du bruit et ne pas les insérer à la suite.

La fonction reveal est aussi beaucoup plus rapide grâce à Christophe qui préfère les bits aux strings.

Photo libre d'Irlande

Petite pause avec la stéganalyse pour tester quelque chose d’un peu différent.

import urllib
import base64
from PIL import Image

def bs(s):
    """
    Converts an int to its bits representation as a string of 0's and 1's.
    """
    return str(s) if s<=1 else bs(s>>1) + str(s&1)

def reveal(img):
    """
    Find a message in an image
    (with the LSB technique).
    """
    width, height = img.size
    bits = ""
    for row in range(height):
        for col in range(width):
            r, g, b = img.getpixel((col, row))
            bits += bs(r)[-1] + bs(g)[-1] + bs(b)[-1]
            if int(bits[-8:], 2) == 126:
                list_of_string_bits = ["".join(list(bits[i:(i+8)])) for i in range(0, len(bits)-8, 8)]
                list_of_character = [chr(int(elem, 2)) for elem in list_of_string_bits]
                return "".join(list_of_character)
    return ""

if __name__ == '__main__':
    # Point of entry in execution mode
    urllib.urlretrieve("http://cedric.bonhomme.free.fr/images/surprise.png", "./surprise.png")
    img = Image.open("./surprise.png")
    surprise = reveal(img)
    data = base64.b64decode(surprise)
    with open("./surprise", "w") as f:
        f.write(data)

Le décodage pourra prendre deux ou trois minutes. Une fois le script terminé, faites un petit file ./surprise. Et vous saurez déjà quoi en faire.

Tout ce que je peux vous dire. 15 Mo is enough! Largement!

Mmm oui, le fichier téléchargé est une photo sous licence Creative Commons. Nous dirons donc que ce fichier peut être partagé.