- Ausgehend von Guy Duponts Experimenten mit winkelabhängigen QR-Codes setzte Christian Walther einen QR-Code um, der auch ohne Linse je nach Scan-Bedingungen als anderes Ziel gelesen wird
- Die Kernstruktur ist eine Schachbrett-Maske, bei der zwei gleich große QR-Codes übereinandergelegt und die einzelnen Pixel aufgeteilt angeordnet werden
- Wenn ein QR-Reader die erwarteten Pixelzentren abtastet, können sich die Sample-Punkte bei einer kleinen Verschiebung stärker zu einem der Codes verlagern und so ein anderes Ergebnis liefern
- In den Experimenten funktionierte das Schachbrett besser als vertikale oder diagonale Streifen; ein hohes Niveau der Fehlerkorrektur war vorteilhaft, dennoch bleiben Unterschiede zwischen Decodern
- Das zeigt, dass auch ein einzelnes QR-Bild je nach Winkel, Verschiebung, Decoder-Implementierung sowie Verarbeitung von Farbe und Alphakanal als mehrere Ergebnisse interpretiert werden kann
Doppelter QR-Code ohne Linse
- Guy Dupont experimentierte mit QR-Codes, die auf Basis der Idee „verschiedene Perspektiven derselben Geschichte“ je nach Winkel zu unterschiedlichen Quellen führen
- Christian Walther probierte aus, ob diese Idee „auch ohne Linse“ möglich ist, und erklärte, ein tatsächlich funktionierendes QR-Bild erstellt zu haben
- Dupont reagierte darauf, dass er in Walthers Ergebnis die beiden Ziele GitHub und Mastodon gelesen habe
- Auch Matt Lacey sagte, auf seinem Smartphone werde die Mastodon-URL besser erfasst, aber auch die GitHub-URL gelesen
Aufbau mit halbierten Pixeln
- Das Grundprinzip besteht darin, zwei gleich große QR-Codes zu erzeugen und sie in einem Bild zu überlagern
- Die eine Hälfte jedes Pixels stammt aus dem einen QR-Code
- Die andere Hälfte stammt aus dem anderen QR-Code
- In der weiteren Erklärung heißt es, ein Code sei in den linken oberen und rechten unteren Quadranten jedes Pixels sichtbar gemacht worden, der andere Code in den übrigen Bereichen
- Als Herstellungsweise wird erwähnt, dass möglicherweise in Photoshop eine Schachbrettmuster-Ebenenmaske gefüllt wurde
Warum die Scan-Ergebnisse auseinandergehen
- Walther nimmt an, dass ein QR-Reader anhand der Ausrichtungsblöcke den zentralen Bereich der erwarteten Pixelpositionen abtastet
- Wenn für alle Pixel dieselbe Maske verwendet wird und das Pixelzentrum auf der Maskengrenze liegt, können sich die Sample-Punkte schon bei einer kleinen Verschiebung gemeinsam in den Bereich eines der Codes verlagern
- Dadurch kann dasselbe Bild je nach Verschiebung als unterschiedlicher QR-Code gelesen werden
- Walther selbst stellt klar, dass diese Sampling-Erklärung eine Vermutung ist und nicht auf einer Untersuchung des tatsächlichen Decoder-Verhaltens beruht
- Ein Nutzer sagte, dass beim direkten Einspeisen der PNG-Datei in den
zbar-tools-Decoder kein Code erkannt wurde
Unterschiede durch Maske und Decoder
- In Walthers Experimenten lieferte die Schachbrett-Maske bessere Ergebnisse als vertikale oder diagonale Streifen
- Mit dem höchsten Niveau der Fehlerkorrektur funktionierte es besser, weitere Experimente seien aber nötig
- Eine diagonale Aufteilung sieht besser aus, in der praktischen Funktion liegt das Schachbrett jedoch vorn
- Auch zwischen Scan-Apps und der Standard-Kamera-App wurden Unterschiede in der Häufigkeit der Ergebnisse gemeldet
- Ein Nutzer sagte, in einer QR-Code-Scanner-App erscheine häufiger der Mastodon-Link, während in der Standard-Kamera-App offenbar beide möglich seien
- Attie Grande sagte, auch zwischen Software-Decodern scheine es Variabilität zu geben
- Hadley sagte, der Code sei nicht gescannt worden, solange das Smartphone still gehalten wurde; als es aber aus einem anderen Winkel bewegt wurde, sei der Scan während der Bewegung stabil gelungen
Variantenexperimente und öffentliche Tools
- Walther stellte im Gespräch dualqrcode.com und das DualQRCode-GitHub-Repository als interaktive Version vor
- xssfox erwähnte einen Ansatz, nahe Kollisionen zu finden, um die Zahl unterschiedlicher Bits zu verringern, und sagte, eine Differenz von 30 Bit per Brute Force zu finden, sehe ziemlich einfach aus
- Walther experimentierte damit, für die beiden QR-Codes unterschiedliche Maskenmuster zu wählen, um die Unterschiede zu reduzieren; der Fall mit derselben Maske für beide Codes habe jedoch die geringsten Unterschiede ergeben
- Auch der ursprüngliche Proof of Concept habe für beide Codes die automatisch vom Generator gewählte mask 6 verwendet, daher habe es in diesem Fall keinen zusätzlichen Vorteil gegeben
- Walther sagte, die iOS-Kamera-App zeige in der Live-Vorschau nur die Domain an und den vollständigen Inhalt erst nach dem Antippen; deshalb habe er absichtlich unterschiedliche Domains gewählt
Farb- und Alphakanal sowie ein Experiment mit vier Codes
- Nemo Thorx teilte ein Experiment, bei dem
42in drei verschiedenen Darstellungen jeweils als QR-Code erstellt und in den RGB-Kanälen zusammengeführt wurde- Rot ist
6x9 - Grün ist
XLII - Blau ist
Forty-two - Später wurde im Gespräch bestätigt, dass sich im Alphakanal ein Code für
㊷befand
- Rot ist
- Walther sagte, er habe das Verhalten des Bildes verstanden, nachdem er den Alphakanal ignoriert hatte
- Als Pixel Dunn versuchte, durch Farbmischung mehrere Codes unterzubringen, hielt Walther es für nicht funktionsfähig, weil ein Decoder bei einer solchen Farbüberblendung nicht vier einzelne Codes trennen könne
- Walther sagte, er habe schon einmal mithilfe beweglicher Ausrichtungsblöcke und Pixelquadranten vier Codes untergebracht
- Wenn man das Ausrichtungsziel ganz leicht verschiebe, werde auf einen anderen Quadranten des Pixels fokussiert
- Eine Methode, bei der sich je nach transparentem Hintergrund und Theme zwei Code-Sets unterscheiden, könnte eventuell möglich sein; Walthers iPhone erkannte sie jedoch weder auf weißem noch auf schwarzem Hintergrund und sie funktionierte nur mit manueller Schwellenwertverarbeitung
1 Kommentare
Hacker-News-Kommentare
Es scheint ein Angriff möglich zu sein, bei dem ein Bildschirm an einem öffentlichen Ort den QR-Code anhand von Informationen über den aktuellen Nutzer leicht verändert, ohne dass er äußerlich stark anders aussieht.
Zum Beispiel könnte man einen halb-halb gemischten QR-Code erzeugen, das Ziel anhand externer Eingaben wie einer kamerabasierten Merkmalsbewertung bestimmen und dann die Farbe des Codes minimal anpassen, um die Wahrscheinlichkeit zu erhöhen, dass er als die gewünschte Variante erkannt wird.
Missbrauchsmöglichkeiten wären etwa, verschiedenen Bevölkerungsgruppen unterschiedliche Feedback-Formulare zu zeigen und so Ergebnisse zu verzerren, Gewinnchancen bei Verlosungen anhand identifizierbarer Merkmale wie Ethnie, Alter oder Aussehen zu verschieben oder nur bestimmte Personen auf ein anderes WLAN-Netz oder eine andere Zahlungsseite zu schicken.
In statischen Umgebungen wäre der Effekt geringer, und mir fällt nicht sofort ein statischer Angriff ein, bei dem man nur einzelne Nutzer herauspickt und daraus einen Vorteil zieht. Unter öffentlicher Beleuchtung würden die meisten wohl kaum bemerken, dass sich ein QR-Code dynamisch verändert.
Der im Original beschriebene Hack scheint vor allem dort praktisch einsetzbar, wo ein Angreifer seinen eigenen Code über einen legitimen QR-Code klebt. Welcher Code gelesen wird, ist dann einigermaßen zufällig, sodass einige Nutzer weiterhin zum ursprünglichen Ziel geschickt werden und die Entdeckung verzögert wird; ob das den Verlust an Traffic auf den eigenen Link ausgleicht, ist aber unklar.
Dafür muss man gar nicht so ausgefeilt sein wie mit diesem doppelten QR-Code. Man geht zu einem „Mit dem Handy bezahlen“-Schild auf einem Parkplatz, klebt den eigenen QR-Code über den „Zum Bezahlen scannen“-QR-Code und wartet darauf, dass Kreditkartendaten eingehen.
Eine andere Methode wäre ein Poster, das nicht völlig flach ist. Wenn man verschiedene Pixel leicht pyramidenförmig hervorstehen lässt und jede Seite anders einfärbt, könnte der QR-Code je nach Betrachtungsrichtung unterschiedlich gescannt werden.
Eine der großen Banken in Tbilisi erlaubt es, IBAN-Nummern per QR-Code zu teilen. Theoretisch ließe sich mit solchen Techniken Geld stehlen, praktisch gibt es aber mehrere Sicherheitsvorkehrungen, etwa dass die Banking-App vor Abschluss der Überweisung den Namen des Empfängers anzeigt.
In meinem Kopf könnte eine solche Steuerung ähnlich funktionieren wie das Erzwingen einer bestimmten Karte, daher dachte ich zuerst eher an Zaubertricks.
Ich habe eine Website gebaut, auf der man es direkt ausprobieren kann: https://dualqrcode.com/
Da Christian die genaue Methodik nicht beschrieben hat, habe ich hier zwei QR-Codes mit einem diagonalen Teilungsmuster zu einem Bild kombiniert. Wenn sich die Muster an derselben Position unterscheiden, wird die Zelle diagonal geteilt: eine Seite steht für den ersten QR-Code, die andere für den zweiten. Wenn beide schwarz oder beide weiß sind, wird die Zelle einfarbig gefüllt.
Dank der hohen Fehlerkorrektur von QR-Codes, insbesondere der Fehlerkorrekturstufe „H“, kann je nach Scanwinkel eine der beiden URLs gelesen werden. Wie in der UI erwähnt, tendiert es allerdings häufiger zur zweiten URL.
Am interessantesten fand ich, dass iOS beim langen Drücken auf das Bild anzeigt, es gehe zu github.com, die Vorschau selbst aber Mastodon zeigt.
Das scheint zu bedeuten, dass der QR-Code zweimal geparst wird und dabei unterschiedliche Ergebnisse herauskommen. Das könnte dazu genutzt werden, einige Nutzer in die Irre zu führen; ich weiß aber nicht, wie viele Menschen die URL im Dropdown nach langem Drücken tatsächlich ansehen.
Deshalb habe ich schon dumme Dinge gemacht wie einen Screenshot per AirDrop auf den Laptop zu schicken und ihn dann mit der Handykamera zu scannen, oder Freunde bzw. Familie zu bitten, den Code auf meinem Handybildschirm mit ihrem Handy zu scannen.
Heute habe ich aber gelernt, dass man in der Kamerarolle auf einem Screenshot einen QR-Code lange drücken kann, woraufhin er direkt geparst und der Link geöffnet wird. Ich glaube, ich hatte früher schon einmal lange auf ein Bild gedrückt; vielleicht wurde die Funktion danach hinzugefügt, vielleicht habe ich es doch nie wirklich ausprobiert, oder vielleicht habe ich auf die falsche Stelle des QR-Codes gedrückt. Jedenfalls ist es sehr erfreulich zu wissen, dass es geht.
Dasselbe ließe sich vermutlich auch mit einem Standard-QR-Code erreichen, in den absichtlich Fehler eingebaut wurden. Man müsste nur herausfinden, wie die beiden Parser Fehler unterschiedlich korrigieren.
Klingt, als hättest du gerade einen Bug-Bounty-Bug gefunden, der nur darauf wartet, eingesammelt zu werden.
Ich denke an Code wie
if someCondition(getFoo()) then doSomethingWith(getFoo())oder an einen Aufruf vondoSomethingWith(getFoo()), gefolgt vondoAnotherThingWith(getFoo()). Im Gegensatz dazu, den Wert einmal mitfoo := getFoo()zu holen und dann zu verwenden, bleibt bei so etwas immer ein Code Smell.Wenn man von der Startposition nach oben scrollt, sieht man die Lentikular-Version.
Sehr eindrucksvoll. Mein iPhone tendierte dazu, auf eine der beiden Varianten einzurasten, und das Drehen des Telefons half, zur einen oder zur anderen zu gelangen.
Ein paar Mal flackerten der Mastodon-Link und der GitHub-Link auch abwechselnd auf.
Wenn man es selbst ausprobieren möchte, kann man diesen schnell geschriebenen Code verwenden. Das Python-Paket
qrcodeinstallieren und den folgenden Code ausführen.Es entsteht tatsächlich ein doppelter QR-Code mit
fooundbar, aber HN kann kein Unicode, und wenn man es durch8oder0ersetzt, erkennt mein Handy ihn nicht, daher lässt er sich schwer direkt einfügen.Für eine Schatzsuche wäre das ziemlich cool.
QR-Codes eignen sich ähnlich gut für Missbrauch wie Kurz-URLs.
Im Grunde ist es eine nicht lesbare URL, sodass man überhaupt nicht weiß, wohin man geschickt wird oder wie oft weitergeleitet wird. Deshalb benutze ich sie nicht.
Wirklich ein sehr cooles Konzept. Wenn man sich auf das Endziel konzentriert, wäre auch ein anderer Ansatz möglich: einen Switch am URL-Ziel.
Dabei würde je nach Zufall, Nutzerdaten oder anderen Kriterien auf unterschiedliche Seiten weitergeleitet. Wenn jemand mit so einer Funktion experimentieren und sogar echte Sticker testen möchte: Ich arbeite mit mehreren SaaS-Unternehmen an variablen Labels und kann Einblicke teilen, Muster drucken oder zusammenarbeiten.