2 points par GN⁺ 2025-01-24 | 1 commentaires | Partager sur WhatsApp
  • En partant de l’expérience de Guy Dupont sur des codes QR selon l’angle, Christian Walther a réussi à créer un code QR qui, même sans lentille, est lu comme menant vers différentes destinations selon les conditions de scan
  • La structure clé est un masque en damier qui superpose deux codes QR de même taille en répartissant leurs pixels
  • Si le lecteur de QR échantillonne le centre des pixels attendus, un léger déplacement de l’image peut faire basculer les points d’échantillonnage vers l’un des deux codes et produire un résultat différent
  • Dans les essais, le damier fonctionnait mieux que des rayures verticales ou diagonales, et un niveau élevé de correction d’erreurs semblait avantageux, même si les résultats variaient encore selon les décodeurs
  • Cela montre qu’une seule image QR peut être interprétée de plusieurs façons selon l’angle, le déplacement, l’implémentation du décodeur, ainsi que le traitement des couleurs et du canal alpha

Un double code QR sans lentille

  • Guy Dupont a expérimenté un code QR qui renvoie vers des sources différentes selon l’angle, à partir de l’idée de « différents points de vue sur une même histoire »
  • Christian Walther a repris cette idée en se demandant si cela pouvait fonctionner sans lentille, et affirme avoir créé une image QR qui marche réellement
  • Dupont a réagi en disant qu’il avait bien lu les deux destinations, GitHub et Mastodon, dans le résultat de Walther
  • Matt Lacey a lui aussi indiqué que, sur son téléphone, l’URL Mastodon était détectée plus facilement, mais que l’URL GitHub était également lisible

Une composition qui partage les pixels en deux

  • Le principe de base consiste à créer deux codes QR de même taille, puis à les superposer en une seule image
    • La moitié de chaque pixel provient d’un des codes QR
    • L’autre moitié provient de l’autre code
  • Dans l’explication, un code est visible dans les quadrants supérieur gauche et inférieur droit de chaque pixel, tandis que l’autre apparaît dans les zones restantes
  • Côté fabrication, il est suggéré qu’un masque de calque à motif en damier a pu être rempli dans Photoshop

Pourquoi le résultat du scan diverge

  • Walther suppose que le lecteur de QR échantillonne la zone centrale de la position de pixel attendue en se basant sur les blocs d’alignement
  • Si le même masque est utilisé pour tous les pixels et que le centre du pixel tombe sur la frontière du masque, un petit déplacement peut faire dériver ensemble les points d’échantillonnage vers la zone de l’un des deux codes
  • Le résultat est qu’une même image peut être lue comme deux codes QR différents selon son déplacement
  • Walther précise lui-même que cette explication sur l’échantillonnage reste une hypothèse, et non le fruit d’une étude du fonctionnement réel des décodeurs
  • Un utilisateur a indiqué qu’en fournissant directement le PNG au décodeur zbar-tools, celui-ci ne détectait pas le code

Les différences créées par le masque et le décodeur

  • Dans les expériences de Walther, le masque en damier donnait de meilleurs résultats que des rayures verticales ou diagonales
  • L’utilisation du niveau maximal de correction d’erreurs semblait mieux fonctionner, même si des essais supplémentaires restent nécessaires
  • La séparation diagonale est plus esthétique, mais le damier la surpasse en pratique
  • Des écarts de fréquence de résultat ont aussi été signalés entre applications de scan et applications photo par défaut
    • Un utilisateur a indiqué que l’application de scanner QR affichait plus souvent le lien Mastodon, tandis que l’application photo par défaut semblait pouvoir lire les deux
    • Attie Grande a observé une variabilité également entre décodeurs logiciels
  • Hadley a expliqué que le code ne se scannait pas lorsqu’il gardait le téléphone immobile, mais qu’il devenait lisible de façon stable pendant le mouvement, une fois l’appareil mis à bouger sous un autre angle

Expériences dérivées et outils publics

  • Au cours de l’échange, Walther a présenté dualqrcode.com et le dépôt GitHub DualQRCode comme versions interactives
  • xssfox a mentionné une approche consistant à rechercher des collisions proches afin de réduire le nombre de bits différents, en ajoutant que trouver en force brute 30 bits d’écart semblait assez simple
  • Walther a testé l’idée de choisir des motifs de masque différents pour les deux codes QR afin de réduire leurs écarts, mais c’est le cas où les deux codes utilisent le même masque qui produisait le moins de différences
    • Le proof of concept d’origine utilisait déjà le mask 6 choisi automatiquement par le générateur pour les deux codes, donc il n’y avait pas de gain supplémentaire dans ce cas
  • Walther a expliqué avoir choisi volontairement deux domaines différents, car l’application photo d’iOS n’affiche que le domaine dans l’aperçu en temps réel et ne montre le contenu complet qu’après un tap

Couleurs, canal alpha et expérience à quatre codes

  • Nemo Thorx a partagé une expérience où trois représentations de 42 étaient chacune converties en code QR puis combinées dans les canaux RGB
    • le rouge contenait 6x9
    • le vert contenait XLII
    • le bleu contenait Forty-two
    • il a ensuite été confirmé dans la discussion que le canal alpha contenait le code
  • Walther a indiqué qu’il avait compris le fonctionnement de cette image une fois le canal alpha ignoré
  • Lorsque Pixel Dunn a voulu insérer plusieurs codes par mélange de couleurs, Walther a estimé que ce type de blending colorimétrique ne permettrait pas au décodeur de séparer quatre codes individuels, et que cela ne fonctionnerait donc pas
  • Walther a dit avoir déjà réussi à intégrer 4 codes en utilisant des blocs d’alignement déplaçables et les quadrants de pixels
    • En déplaçant très légèrement les cibles d’alignement, on peut focaliser sur d’autres quadrants des pixels
  • Une méthode où deux ensembles de codes changeraient selon le fond transparent et le thème pourrait éventuellement être possible, mais l’iPhone de Walther ne reconnaissait ni la version sur fond blanc ni celle sur fond noir, et cela ne marchait qu’après application manuelle d’un traitement par seuillage

1 commentaires

 
GN⁺ 2025-01-24
Avis sur Hacker News
  • Il semble possible de mener une attaque où un écran dans un lieu public modifie légèrement un code QR en fonction des informations de l’utilisateur actuel, sans que cela paraisse très différent visuellement
    Par exemple, on pourrait créer un code QR mélangé moitié-moitié, choisir une cible à partir d’entrées externes comme une évaluation de caractéristiques basée sur la caméra, puis ajuster subtilement les couleurs du code pour augmenter la probabilité qu’il soit reconnu dans le sens voulu
    Parmi les usages malveillants possibles : montrer des formulaires de retour différents selon les groupes démographiques pour biaiser les résultats, favoriser les chances de gagner à un jeu-concours selon des caractéristiques identifiantes comme l’origine ethnique, l’âge ou l’apparence, ou encore envoyer seulement certaines personnes vers un autre réseau Wi‑Fi ou une autre page de paiement
    Dans un environnement statique, l’effet est moindre, et je ne vois pas immédiatement d’attaque statique qui ne viserait qu’à soutirer un avantage à une partie des utilisateurs. Dans un éclairage public, la plupart des gens auraient sans doute du mal à remarquer qu’un code QR change dynamiquement

    • Ce genre de chose peut être géré bien plus discrètement côté serveur, donc je ne vois pas très bien ce que la modification du code QR apporte de plus
      Le cas où la technique de piratage d’origine pourrait réellement servir me semble être celui où l’attaquant colle son propre code par-dessus un code QR légitime. Comme la lecture de l’un ou l’autre code serait un peu aléatoire, certains utilisateurs seraient envoyés vers la destination d’origine, ce qui pourrait retarder la détection, mais on ne sait pas si cela compenserait la baisse de trafic vers le lien malveillant
    • Les attaques consistant à remplacer un code QR public existent déjà en pratique
      Il n’est même pas nécessaire d’aller jusqu’à quelque chose d’aussi sophistiqué que ce double code QR. Il suffit d’aller à un panneau de parking « payer avec votre téléphone », de coller son propre QR sur celui « scanner pour payer », puis d’attendre que des données de carte bancaire arrivent
    • Dans le même esprit, on pourrait coller une affiche avec un code QR sur le mur d’une pièce équipée de deux ampoules LED. Si les deux ampoules ont des températures de couleur différentes, le résultat du décodage pourrait varier selon laquelle est allumée
      Une autre méthode consisterait à utiliser une affiche qui n’est pas parfaitement plane. Si différents pixels ressortent légèrement en forme de pyramide et que chaque face est peinte différemment, le code QR pourrait être scanné différemment selon l’angle de vue
      L’une des principales banques de Tbilissi permet de partager un numéro IBAN via un code QR. En théorie, on pourrait voler de l’argent avec ce type de technique, mais en pratique l’application bancaire affiche le nom du destinataire avant de finaliser le virement, entre autres garde-fous
    • Je pense qu’une personne ordinaire ne remarquerait pas qu’un code QR moyen a été fortement modifié. La plupart des formats lisibles par machine ressemblent à du bruit blanc impossible à distinguer à l’œil nu
    • C’est bien que les usages malveillants soient détaillés. Avec la seule configuration, je n’aurais probablement pas pensé à ces applications
      Dans ma tête, ce type de contrôle pourrait aussi servir à forcer le choix d’une carte précise, donc j’ai d’abord pensé à des tours de magie
  • J’ai créé un site pour que l’on puisse tester directement : https://dualqrcode.com/
    Comme Christian n’a pas publié de méthodologie exacte, j’ai ici fusionné deux codes QR en une seule image avec un motif de séparation en diagonale. Quand les motifs diffèrent au même emplacement, la cellule est divisée en diagonale : un côté représente le premier code QR, l’autre le second ; si les deux sont noirs ou blancs, la cellule est remplie d’une seule couleur
    Grâce à la forte capacité de correction d’erreurs des codes QR, en particulier avec le niveau de correction « H », l’un ou l’autre des URL peut être lu selon l’angle de scan. En revanche, comme indiqué dans l’interface, le second URL est lu plus souvent

    • Chez moi, cela n’a pas marché. J’ai créé deux liens BBC et CNN et essayé avec l’application Android « QR & Barcode Scanner » v2.2.47 ainsi qu’avec l’appareil photo par défaut d’un iPhone 13, mais aucun des deux n’a réussi à les lire
  • Sur iOS, un appui long sur l’image indique que cela mène vers github.com, alors que l’aperçu lui-même affiche Mastodon ; c’est ce qui m’a le plus intrigué
    Cela semble vouloir dire que le code QR est analysé deux fois et produit des résultats différents. On pourrait s’en servir pour tromper certains utilisateurs, mais je ne sais pas combien de personnes consultent réellement l’URL du menu déroulant après un appui long

    • Il m’arrive parfois d’avoir un code QR affiché directement sur l’écran du téléphone que je suis en train d’utiliser, ce qui est assez gênant
      Du coup, j’ai déjà fait des choses absurdes comme envoyer une capture d’écran par AirDrop vers mon ordinateur portable pour la scanner avec l’appareil photo du téléphone, ou demander à un ami ou à un membre de ma famille de scanner le code affiché sur mon écran avec son téléphone
      Mais j’ai appris aujourd’hui qu’il suffit d’effectuer un appui long sur un code QR dans une capture d’écran de la pellicule pour qu’il soit analysé immédiatement et que le lien s’ouvre. J’ai peut-être déjà essayé d’appuyer longuement sur une image par le passé, mais soit la fonctionnalité a été ajoutée ensuite, soit je ne l’avais jamais vraiment fait, soit j’appuyais sur la mauvaise partie du code QR. Quoi qu’il en soit, je suis très content d’apprendre que c’est possible
    • Il est tout à fait plausible que deux parties du système d’exploitation utilisent chacune un parseur QR différent. SmartText en utilise un, le système d’images un autre, par exemple. Leurs implémentations de correction d’erreurs semblent légèrement différentes
      On pourrait probablement obtenir le même effet avec un code QR standard contenant des erreurs intentionnelles. Il suffirait de déterminer comment chacun corrige différemment les erreurs
      On dirait une faille de bug bounty qui n’attend plus que quelqu’un la récupère
    • Les codes QR devraient aussi afficher l’URL cible en clair, afin que l’utilisateur sache où il va. Cela devrait servir de forme de consentement explicite
    • Sur iOS, avec un appui long, le lien par défaut « Open » mène à Mastodon, et le menu contextuel affiche aussi un lien « Open in Github » vers l’application
    • Cela ressemble à un cas typique où l’on appelle deux fois la même fonction ou le même getter, alors que les deux appels devraient impérativement renvoyer le même résultat
      Cela fait penser à du code comme if someCondition(getFoo()) then doSomethingWith(getFoo()), ou à doSomethingWith(getFoo()) suivi d’un appel à doAnotherThingWith(getFoo()). Contrairement à une approche du type foo := getFoo() où l’on récupère la valeur une fois pour la réutiliser, ce genre de chose laisse toujours une odeur de code
  • En faisant défiler vers le haut depuis la position de départ, on peut voir la version lenticulaire

  • C’est extrêmement saisissant. Mon iPhone avait tendance à se verrouiller sur l’un des deux, et faire pivoter le téléphone aidait à partir vers l’un ou l’autre
    À plusieurs reprises, les liens Mastodon et GitHub se sont même mis à clignoter en alternance

  • Si vous voulez essayer vous-même, vous pouvez utiliser un bout de code bricolé rapidement. Il suffit d’installer le package Python qrcode et d’exécuter le code ci-dessous
    On obtient bien un double code QR avec foo et bar, mais HN ne gère pas l’Unicode, et si on remplace par 8 ou 0, mon téléphone ne le reconnaît pas, donc il est difficile de le coller tel quel

    import qrcode
    
    bar = qrcode.QRCode(border=0)  
    bar.add_data('bar')  
    bar.make()  
    bar_mat = bar.get_matrix()
    
    foo = qrcode.QRCode(border=0)  
    foo.add_data('foo')  
    foo.make()  
    foo_mat = foo.get_matrix()
    
    for l, r in zip(foo_mat, bar_mat):  
        line = ''  
        for lc, rc in zip(l, r):  
            line = line + (lc and '\u2588' or ' ')  
            line = line + (rc and '\u2588' or ' ')  
        print(line)  
        print(line)  
    
  • Ce serait génial pour une chasse au trésor

    • On pourrait aussi faire en sorte qu’il faille les résultats de deux, voire de tous les codes QR, pour obtenir le code, le lien ou la clé finale
  • Les codes QR sont parfaits pour les abus, comme les URL raccourcies
    Ce sont essentiellement des URL illisibles : impossible de savoir où elles mènent ni combien de redirections elles effectueront. C’est pour cela que je ne les utilise pas

  • Concept vraiment excellent. Si l’on se concentre sur l’objectif final, une autre approche serait aussi de mettre un commutateur au niveau de la destination URL
    L’idée serait de rediriger vers des pages différentes selon l’aléatoire, les données utilisateur ou d’autres critères. Si vous voulez expérimenter ce genre de fonctionnalité et même faire de vrais tests sur autocollants, je travaille avec plusieurs entreprises SaaS et sur des projets d’étiquettes variables ; je peux donc partager des idées, imprimer des échantillons ou collaborer