- Un développeur a mené une expérience consistant à considérer le favicon, l’icône d’onglet du navigateur, comme un stockage de bytes par pixel, et à y placer un petit HTML dans les canaux RGB de l’image
- L’encodage consiste à ajouter un en-tête de 4 bytes devant les bytes UTF-8 du HTML, puis à écrire chaque byte successivement dans les valeurs R, G et B des pixels
- Le payload de démonstration fait 208 bytes, et 212 bytes avec l’en-tête ; 71 pixels stockant 3 bytes chacun, ainsi qu’un PNG 9×9, suffisaient donc
- Pour la restauration, l’image du favicon est dessinée sur un canvas, puis JavaScript lit les données de pixels et réassemble les valeurs RGB en tableau de bytes avant de les décoder en HTML
- La structure ne permet pas au site web de s’exécuter de manière autonome avec le seul favicon ; un bootstrap JavaScript séparé est nécessaire, ce qui en fait davantage une expérimentation aux limites qu’une approche pratique
Comment traiter un favicon comme un espace de stockage
- Un favicon est une petite icône affichée dans l’onglet du navigateur, mais c’est en réalité un fichier image composé de pixels et de bytes
- L’expérience part de la stéganographie, mais dans cette démo, l’objectif n’est pas de ressembler à une icône, plutôt d’utiliser l’image comme pur espace de stockage
- La cible de stockage est un petit payload HTML
Website in a Favicon
Everything you're reading right now was decoded from favicon pixels.
- La procédure d’encodage est simple
- conversion du HTML en bytes UTF-8 avec
TextEncoder
- ajout, au début, d’un en-tête de 4 bytes contenant la longueur du payload
- comme il peut rester des pixels inutilisés, l’en-tête de longueur permet d’identifier la fin réelle du payload
- le premier byte est stocké dans le canal red du premier pixel, le deuxième dans green, le troisième dans blue
- les pixels suivants sont remplis dans le même ordre, de sorte que l’intégralité du document HTML est encodée dans les valeurs de couleur
- L’image résultante ressemble visuellement à du bruit
Taille et processus de restauration
- La taille finale de la démo est très petite
- payload : 208 bytes
- total avec l’en-tête : 212 bytes
- pixels nécessaires : 71 pixels
- dimensions de l’image : 9×9 pixels
- taille : 239 bytes
- taux d’utilisation : 87% {p:87}
- La restauration se fait uniquement avec les fonctionnalités du navigateur
- chargement du favicon comme image
- dessin de l’image sur un canvas
- lecture de tous les pixels via la Canvas API
- reconstruction d’un tableau de bytes à partir des valeurs RGB
- lecture de la longueur du payload dans les 4 premiers bytes
- extraction du payload et décodage en texte UTF-8
- Sur le site de démonstration, un clic sur le bouton
"Render Website" lit le favicon, restaure le HTML, puis remplace le contenu de la page
Limites et alternatives
- La contrainte la plus importante est qu’un favicon ne peut pas, à lui seul, exécuter l’ensemble du site web
- le favicon contient le contenu du site web
- un petit chargeur JavaScript dédié au décodage est nécessaire en plus
- sans JavaScript, le favicon n’est qu’un PNG contenant le contenu du site web
- L’intérêt pratique est limité
- la quantité de données stockable est très faible
- la page doit être bootstrapée en JavaScript
- il existe de bien meilleures façons de distribuer un petit document HTML
- Parmi les alternatives : insérer directement du markup dans un favicon SVG, utiliser les chunks de commentaire
tEXt, zTXt, iTXt d’un PNG, ou le format de fichier ico, qui peut contenir des icônes de plusieurs résolutions
- Site de démo : https://www.timwehrle.de/labs/favicon-site/
- Code d’implémentation : https://github.com/timwehrle/favicon
1 commentaires
Commentaires sur Hacker News
Au lieu de passer par des pixels, on pourrait simplement utiliser un favicon SVG, y stocker directement le balisage, puis l’extraire
Il suffirait de mettre quelque chose comme
hello HN!dansfavicon.svg, de l’utiliser comme favicon SVG, puis de l’extraire et de l’injecter dans le corps du documentSinon, on peut aussi servir directement le fichier SVG et y inclure du HTML embarqué. En théorie, on devrait pouvoir le définir puis l’utiliser, mais en pratique ni Firefox ni Chromium ne semblent bien le gérer dans un favicon, ce qui est dommage
[\s\S]peut s’écrire de manière plus courte et plus exacte avec[^]On peut donc empiler une couche supplémentaire d’expérimentation : un favicon en SVG, contenant un raster encodé, dont les octets contiennent eux-mêmes du HTML encodé. Au minimum, ça ferait une étape de CTF assez hallucinante
Bien sûr, ce n’est pas une idée nouvelle. Par exemple, quelqu’un a stocké deCSS dans un favicon en 2000
https://web.archive.org/web/20010408040524if_/http://decss.z...
L’extraction peut se faire avec
dd bs=1 skip=2238 < favicon.icoCela ne veut pas dire qu’« il faut encore un petit bootstrap loader pour décoder l’image ». Avec un polyglot HTML/PNG, on peut tout faire tenir dans un seul fichier, et avec des formats plus récents comme WebP, on pourrait même obtenir de meilleurs taux de compression
https://web.archive.org/web/20120801001616/http://daeken.com...
Si l’on redirige l’utilisateur vers plusieurs domaines, le cache des favicons peut aussi servir de stockage. Cela a déjà été proposé comme risque potentiel de fingerprinting[0], et si le navigateur réutilise naïvement ce cache même en mode privé, cela peut servir au pistage inter-profils
[0]: https://www.schneier.com/blog/archives/2021/02/browser-track...
Le lien vers le site de supercookie est malheureusement mort
Le PNG a des chunks de commentaire tEXt, zTXt, iTXt. On peut y fourrer autant de contenu qu’on veut dans un fichier image en apparence totalement banal. Bon, c’est un peu moins amusant, certes
Quel timing, c’est une coïncidence ? Il y a tout juste une heure, ou plutôt 30 minutes avant cet article, j’ai posté un site qui stocke un portefeuille d’actions dans l’URL + le favicon
https://news.ycombinator.com/item?id=48606396
« Pong in S Favicon »
https://news.ycombinator.com/item?id=48608681
Cela correspond parfaitement à cette manière de penser : le moniteur est aussi un support de stockage, le clavier aussi, et les messages de forum aussi
En introduisant au fil du temps des variations éditoriales dont Markov approuverait le style, on obtient une capacité de stockage assez importante. Et comme les commentaires sont parfois socialement intéressants, cela fait un support de stockage à double usage.
Personne ne sait si la recette de gratin de poulet de quelqu’un n’est pas en réalité le handle d’un GUID soigneusement construit qui pointe, pour plaisanter, vers mille messages de forum différents. Je me demande si l’auteur connaît PoC||GTFO, car c’est clairement le genre de technique qu’on s’attend à trouver dans les profondeurs du livre sacré des Alchemist Owls
Le style, très haché et agressif, donnait clairement l’impression d’un texte généré par un LLM, et c’était très pénible à lire
Pour moi, l’auteur essaie juste d’aller droit au but. Il semble savoir que quand il y a trop de texte, les gens se mettent à survoler
Il s’est trompé entre
it’s/its, a fait deBut.une phrase d’un seul mot, n’a pas écrit HTML en majuscules, et a mis « okayy » entre parenthèses. Ce n’est pas pour critiquer l’auteur, au contraire : j’ai trouvé agréable de voir ces petites imperfections qui composent un billet de blogÇa m’a rappelé le real pixel coding d’Inigo : https://www.youtube.com/watch?v=FvS_DG8yIqQ
C’est une intro de 256 octets réalisée en plaçant les pixels dans Photoshop puis en l’enregistrant comme exe
Fait amusant : n’importe quel SVG inline peut être utilisé comme favicon et laissé tel quel dans un document HTML
Cela permet aussi d’utiliser directement un emoji comme favicon. Sur HN, les emojis ne s’affichent pas
#rrggbbou des liensurl(#id)de cette manière, il faut échapper#en%23. Sinon, ce sera analysé comme un fragment d’URL et le code SVG sera tronqué à cet endroit