- Der Entwickler betrachtete das Favicon, also das Browser-Tab-Symbol, als Pixeldatenspeicher und führte ein Experiment durch, bei dem kleines HTML in den RGB-Kanälen eines Bildes abgelegt wurde
- Die Kodierung funktioniert so, dass vor die UTF-8-Bytes des HTML ein 4-Byte-Längenheader gesetzt wird und danach jedes Byte der Reihe nach in die R-, G- und B-Werte der Pixel geschrieben wird
- Die Demo-Payload ist 208 Byte groß und mit Header 212 Byte, sodass 71 Pixel mit je 3 gespeicherten Byte und ein 9×9-PNG ausreichten
- Zur Wiederherstellung wird das Favicon-Bild auf ein Canvas gezeichnet, danach liest JavaScript die Pixeldaten aus und setzt die RGB-Werte wieder zu einem Byte-Array zusammen, das als HTML dekodiert wird
- Die Struktur, bei der allein das Favicon eine Website unabhängig ausführt, ist nicht möglich; es wird zusätzliches Bootstrap-JavaScript benötigt, weshalb es eher ein Grenzexperiment als etwas Praktisches ist
Wie man ein Favicon wie einen Datenspeicher behandelt
- Ein Favicon ist zwar das kleine Symbol im Browser-Tab, tatsächlich aber eine Bilddatei, die aus Pixeln und Bytes besteht
- Der Ausgangspunkt des Experiments war Steganografie, in der Demo liegt der Fokus jedoch nicht darauf, wie ein Icon auszusehen, sondern darauf, es als reinen Speicherplatz zu verwenden
- Gespeichert wird eine kleine HTML-Payload
Website in a Favicon
Everything you're reading right now was decoded from favicon pixels.
- Das Kodierungsverfahren ist einfach
- Mit
TextEncoder wird das HTML in UTF-8-Bytes umgewandelt
- Davor wird ein 4-Byte-Header mit der Länge der Payload gesetzt
- Da Pixel übrig bleiben können, dient der Längenheader dazu, das tatsächliche Ende der Payload zu bestimmen
- Das erste Byte wird im roten Kanal des ersten Pixels gespeichert, das zweite im grünen, das dritte im blauen
- Danach werden die weiteren Pixel in derselben Reihenfolge gefüllt, sodass das gesamte HTML-Dokument in Farbwerten steckt
- Das resultierende Bild sieht visuell wie Rauschen aus
Größe und Wiederherstellungsprozess
- Die endgültige Größe der Demo ist sehr klein
- Payload: 208 bytes
- Gesamtmenge mit Header: 212 bytes
- Benötigte Pixel: 71 pixels
- Bildgröße: 9×9 pixels
- Dateigröße: 239 bytes
- Auslastung: 87% {p:87}
- Die Wiederherstellung erfolgt allein mit Browser-Funktionen
- Das Favicon wird als Bild geladen
- Das Bild wird auf ein Canvas gezeichnet
- Mit der Canvas API werden alle Pixel ausgelesen
- Die RGB-Werte werden wieder zu einem Byte-Array zusammengesetzt
- Aus den ersten 4 Byte wird die Länge der Payload gelesen
- Die Payload wird extrahiert und als UTF-8-Text dekodiert
- Auf der Demo-Seite liest ein Klick auf die Schaltfläche
"Render Website" das Favicon ein, stellt das HTML wieder her und ersetzt dann den Seiteninhalt
Grenzen und Alternativen
- Die größte Einschränkung ist, dass ein Favicon nicht allein eine vollständige Website ausführen kann
- Das Favicon enthält den Inhalt der Website
- Ein kleiner JavaScript-Loader zum Dekodieren wird zusätzlich benötigt
- Ohne JavaScript ist das Favicon nur ein PNG mit Website-Inhalt
- Der praktische Nutzen ist gering
- Die speicherbare Datenmenge ist sehr klein
- Die Seite muss per JavaScript gebootstrapped werden
- Es gibt viele bessere Wege, kleine HTML-Dokumente zu verteilen
- Als Alternativen werden genannt: Markup direkt in ein SVG-Favicon einbetten, die PNG-Comment-Chunks
tEXt, zTXt, iTXt verwenden oder das ico-Dateiformat nutzen, das Icons in mehreren Auflösungen enthalten kann
- Demo-Seite: https://www.timwehrle.de/labs/favicon-site/
- Implementierungscode: https://github.com/timwehrle/favicon
1 Kommentare
Hacker-News-Kommentare
Man könnte doch statt über Pixel zu gehen einfach ein SVG-Favicon verwenden, das Markup direkt darin speichern und anschließend extrahieren.
Man packt etwas wie
hello HN!infavicon.svg, verwendet es als SVG-Favicon und extrahiert es dann in den Dokument-Body.Oder man liefert die SVG-Datei direkt aus und bettet HTML ein. Theoretisch sollte man es definieren und dann verwenden können, aber in der Praxis scheinen weder Firefox noch Chromium das innerhalb eines Favicons richtig zu verarbeiten, was schade ist.
[\s\S]kann man kürzer und präziser als[^]schreiben.Man könnte das Experiment also noch eine Ebene weiter treiben: ein SVG-Favicon, darin ein kodiertes Rasterbild, und in dessen Bytes wiederum kodiertes HTML. Das wäre zumindest eine ziemlich verwirrende CTF-Stufe.
Natürlich ist das keine neue Idee. Zum Beispiel hat schon 2000 jemand deCSS in einem Favicon gespeichert.
https://web.archive.org/web/20010408040524if_/http://decss.z...
Extrahieren lässt es sich mit
dd bs=1 skip=2238 < favicon.ico.Es stimmt auch nicht, dass man „immer noch einen kleinen Bootstrap-Loader zum Dekodieren des Bildes braucht“. Mit einem HTML/PNG-Polyglot lässt sich alles in einer einzigen Datei unterbringen, und mit neueren Formaten wie WebP könnte die Kompression heute sogar noch besser sein.
https://web.archive.org/web/20120801001616/http://daeken.com...
Wenn man Nutzer über mehrere Domains umleitet, kann man auch den Favicon-Cache als Speicher verwenden. Das wurde schon einmal als potenzielles Fingerprinting-Risiko vorgeschlagen[0], und wenn Browser den Cache selbst im privaten Modus naiv wiederverwenden, könnte das zur Nutzerverfolgung über Browser-Profile hinweg missbraucht werden.
[0]: https://www.schneier.com/blog/archives/2021/02/browser-track...
Der Link zur Supercookie-Seite ist leider tot.
PNG hat Kommentar-Chunks vom Typ tEXt, zTXt, iTXt. In eine äußerlich völlig normale Bilddatei kann man also beliebig viel Inhalt stopfen. Zugegeben, der Spaßfaktor ist etwas geringer.
Ist das Timing Zufall? Ich habe gerade vor einer Stunde, genauer gesagt 30 Minuten vor diesem Beitrag, eine Website gepostet, die mein Aktienportfolio in URL + Favicon speichert.
https://news.ycombinator.com/item?id=48606396
„Pong in S Favicon“
https://news.ycombinator.com/item?id=48608681
Das passt wirklich gut zu dieser Denkweise: Auch Monitore sind Speicher, Tastaturen sind Speicher, und Forenbeiträge sind Speicher.
Wenn man über die Zeit beim Bearbeiten Variationen einstreut, die Markov gefallen würden, kommt da ziemlich viel Speicherkapazität zusammen. Außerdem sind Kommentare manchmal auch sozial interessant, also ist es ein Speicher mit Doppelnutzen.
Niemand weiß, ob das Hähnchenauflauf-Rezept von irgendwem in Wahrheit der Handle einer kunstvoll konstruierten GUID ist und scherzhaft gesagt auf tausend verschiedene Forenbeiträge verweist. Ich frage mich, ob der Autor PoC||GTFO kennt, denn das ist definitiv eine Technik, die man tief in den heiligen Büchern der Alchemist Owls finden würde.
Dieser aggressiv abgehackte Stil, der offensichtlich wie von einem LLM erzeugt wirkt, war wirklich sehr schwer zu lesen.
Auf mich wirkt es einfach so, als wolle der Autor direkt zum Punkt kommen. Er scheint zu wissen, dass Leute bei zu viel Text nur noch drüberfliegen.
it’s/itswurde verwechselt,But.steht als Ein-Wort-Satz da, HTML wurde nicht großgeschrieben, und in Klammern steht „okayy“. Das soll keine Kritik am Autor sein; im Gegenteil, ich fand es gerade angenehm, diese kleinen Unsauberkeiten zu sehen, aus denen Blogposts bestehen.Mich hat das an Inigos real pixel coding erinnert: https://www.youtube.com/watch?v=FvS_DG8yIqQ
Ein 256-Byte-Intro, das in Photoshop durch Platzieren von Pixeln erstellt und als exe gespeichert wurde.
Fun Fact: Jedes Inline-SVG kann als Favicon verwendet und unverändert im HTML-Dokument belassen werden.
Damit kann man sogar Emojis direkt als Favicon verwenden. Auf HN werden Emojis allerdings nicht angezeigt.
#rrggbb-Farbcodes oderurl(#id)-Links verwenden will, muss man#als%23escapen. Sonst wird es als URL-Fragment geparst und der SVG-Code an dieser Stelle abgeschnitten.