2 puntos por GN⁺ 2025-01-24 | 1 comentarios | Compartir por WhatsApp
  • A partir del experimento de Guy Dupont con códigos QR que varían según el ángulo, Christian Walther implementó un código QR que se lee como distintos destinos según las condiciones de escaneo, incluso sin lentes
  • La estructura central es una máscara de tablero de ajedrez que superpone 2 códigos QR del mismo tamaño y divide cada píxel para distribuirlos
  • Si el lector de QR muestrea el centro esperado de cada píxel, cuando la imagen se desplaza un poco los puntos de muestreo pueden concentrarse hacia uno de los códigos y producir un resultado distinto
  • En los experimentos, el patrón de tablero de ajedrez funcionó mejor que las franjas verticales o diagonales; un nivel alto de corrección de errores ayudó, pero siguen existiendo diferencias entre decodificadores
  • Muestra que una sola imagen QR puede interpretarse como varios resultados según el ángulo, el desplazamiento, la implementación del decodificador y la forma de procesar colores y canal alfa

Código QR doble hecho sin lentes

  • Guy Dupont experimentó con un código QR que, bajo la idea de “distintos puntos de vista de la misma historia”, envía a distintas fuentes según el ángulo
  • Christian Walther intentó comprobar si esta idea era “posible incluso sin lentes” y dijo que logró crear una imagen QR que funciona en la práctica
  • Dupont reaccionó diciendo que en el resultado de Walther leyó dos destinos: GitHub y Mastodon
  • Matt Lacey también dijo que en su teléfono se detectaba mejor la URL de Mastodon, pero que también se leía la URL de GitHub

Una composición que divide los píxeles por mitades

  • El método básico consiste en crear 2 códigos QR del mismo tamaño y superponerlos en una sola imagen
    • La mitad de cada píxel proviene de un código QR
    • La otra mitad proviene del otro código QR
  • En la explicación posterior, se indica que un código se hacía visible en los cuadrantes superior izquierdo e inferior derecho de cada píxel, y el otro en las zonas restantes
  • Como método de producción, se menciona la posibilidad de haber rellenado en Photoshop una máscara de capa con patrón de tablero de ajedrez

Por qué se dividen los resultados del escaneo

  • Walther supone que el lector de QR muestrea el centro de las posiciones de píxel esperadas tomando como referencia los bloques de alineación
  • Si se usa la misma máscara en todos los píxeles y el centro del píxel queda sobre el borde de la máscara, incluso un pequeño desplazamiento puede hacer que los puntos de muestreo se inclinen en conjunto hacia la región de uno de los códigos
  • Como resultado, la misma imagen puede leerse como códigos QR distintos según la magnitud del desplazamiento
  • Walther aclaró que esta explicación del muestreo es una suposición, no el resultado de haber estudiado el funcionamiento real de los decodificadores
  • Un usuario dijo que, al pasar el PNG directamente al decodificador zbar-tools, no logró detectar el código

Diferencias generadas por la máscara y el decodificador

  • En los experimentos de Walther, la máscara de tablero de ajedrez produjo mejores resultados que las franjas verticales o diagonales
  • Funcionó mejor al usar el nivel más alto de corrección de errores, aunque señaló que hacen falta más pruebas
  • La división diagonal se ve mejor, pero en términos de funcionamiento real el tablero de ajedrez la supera
  • También se reportaron diferencias en la frecuencia de resultados entre apps de escaneo y la app de cámara predeterminada
    • Un usuario dijo que en una app de escáner QR aparecía con más frecuencia el enlace de Mastodon, mientras que en la app de cámara predeterminada parecían posibles ambos
    • Attie Grande dijo que también parece haber variabilidad entre decodificadores de software
  • Hadley dijo que no se escaneaba cuando mantenía el teléfono quieto, pero que al empezar a moverlo hacia otros ángulos se escaneaba de forma estable durante el movimiento

Experimentos derivados y herramientas públicas

  • En la conversación, Walther presentó dualqrcode.com y el repositorio de GitHub DualQRCode como versiones interactivas
  • xssfox mencionó un enfoque de buscar colisiones cercanas para reducir la cantidad de bits diferentes, y dijo que encontrar por fuerza bruta una diferencia de 30 bits parecía bastante simple
  • Walther probó reducir las diferencias eligiendo distintos patrones de máscara para los dos códigos QR, pero dijo que usar la misma máscara en ambos códigos fue lo que produjo la menor diferencia
    • También dijo que en el proof of concept original no hubo beneficio adicional en ese caso, porque el generador había elegido automáticamente mask 6 para ambos códigos
  • Walther dijo que eligió deliberadamente dominios distintos porque la app de cámara de iOS solo muestra el dominio en la vista previa en tiempo real y exige tocar para ver el contenido completo

Canal de color y alfa, y experimento con 4 códigos

  • Nemo Thorx compartió un experimento en el que creó códigos QR para 42 expresado de tres maneras y los combinó en los canales RGB
    • Rojo es 6x9
    • Verde es XLII
    • Azul es Forty-two
    • Más adelante en la conversación se confirmó que en el canal alfa estaba el código
  • Walther dijo que entendió cómo funcionaba esa imagen al ignorar el canal alfa
  • Cuando Pixel Dunn intentó insertar varios códigos mediante mezcla de colores, Walther consideró que esa forma de blending de color no funcionaría porque el decodificador no podría separar 4 códigos individuales
  • Walther dijo que alguna vez insertó 4 códigos usando bloques de alineación móviles y cuadrantes de píxeles
    • Dijo que al mover muy ligeramente el objetivo de alineación se enfoca otro cuadrante del píxel
  • Un método en el que dos conjuntos de códigos cambien según el fondo transparente y el tema podría ser posible, pero el iPhone de Walther no lo reconoció ni con fondo blanco ni con fondo negro; solo funcionó al aplicar procesamiento de umbral manual

1 comentarios

 
GN⁺ 2025-01-24
Opiniones en Hacker News
  • Parece posible un ataque en el que una pantalla en un lugar público modifique ligeramente un código QR según la información del usuario actual, sin que por fuera parezca muy diferente.
    Por ejemplo, se podría crear un código QR mezclado mitad y mitad y, tras elegir un objetivo con alguna entrada externa como una evaluación de características basada en cámara, ajustar mínimamente el color del código para aumentar la probabilidad de que se reconozca el lado deseado.
    Entre los posibles abusos estarían mostrar formularios de feedback distintos según grupos demográficos para distorsionar los resultados, sesgar la probabilidad de ganar premios según rasgos identificables como raza, edad o apariencia, o enviar solo a ciertas personas a otra red Wi‑Fi o página de pago.
    En entornos estáticos sería menos efectivo, y no se me ocurre de inmediato un ataque estático que saque provecho desviando solo a algunos usuarios. Bajo iluminación pública, a la mayoría le costaría notar que el código QR cambia dinámicamente.

    • Algo así podría manejarse de forma mucho más discreta del lado del servidor, así que no veo bien qué aporta modificar el código QR.
      Donde la técnica de hackeo original sí parece tener uso real es cuando el atacante pega su propio código encima de un QR legítimo. Como cuál de los códigos se lee sería algo aleatorio, algunos usuarios podrían ser enviados al destino original y eso retrasaría la detección, aunque no queda claro si compensaría la pérdida de tráfico hacia el enlace del atacante.
    • Ya existen ataques reales que modifican códigos QR públicos.
      Ni siquiera hace falta algo tan sofisticado como este doble código QR. Basta con ir a un letrero de “paga con tu celular” en un estacionamiento, pegar tu propio QR encima del código de “escanea para pagar” y esperar a que lleguen datos de tarjetas de crédito.
    • De forma similar, se podría pegar un póster con un código QR en la pared de una habitación con dos focos LED. Si los dos focos tienen temperaturas de color distintas, el resultado decodificado podría cambiar según cuál esté encendido.
      Otro método sería un póster que no sea completamente plano. Si distintos píxeles sobresalen un poco en forma de pirámide y se pinta cada cara de manera diferente, el código QR podría escanearse distinto según el ángulo de visión.
      Uno de los principales bancos de Tbilisi permite compartir el número IBAN mediante un código QR. En teoría, con una técnica así se podría robar dinero, pero en la práctica la app bancaria tiene varias medidas de seguridad, como mostrar el nombre del destinatario antes de completar la transferencia.
    • Creo que una persona común no notaría aunque un código QR promedio cambiara bastante. La mayoría de los formatos de lectura mecánica se ven para el ojo humano como ruido blanco indistinguible.
    • Me gustó que explicaran incluso los posibles abusos. Si solo hubiera visto la configuración, probablemente no se me habrían ocurrido esos usos.
      En mi cabeza, este tipo de control podría funcionar como forzar la elección de una carta específica, así que lo primero que se me vino a la mente fueron los trucos de magia.
  • Hice un sitio web para que se pueda probar directamente: https://dualqrcode.com/
    Como no tenía la metodología exacta que usó Christian, aquí combiné dos códigos QR en una sola imagen con un patrón de división diagonal. Si los patrones en la misma posición difieren, la celda se divide en diagonal: un lado representa el primer código QR y el otro el segundo; si ambos son negros o blancos, se rellena con un solo color.
    Gracias a la alta capacidad de corrección de errores de los códigos QR, especialmente el nivel de corrección ‘H’, puede leerse cualquiera de las dos URL según el ángulo de escaneo. Eso sí, como se indica en la UI, suele sesgarse más hacia la segunda URL.

    • En mi entorno no funcionó. Generé dos enlaces, BBC y CNN, y probé con la app “QR & Barcode Scanner” v2.2.47 en Android y con la cámara predeterminada de un iPhone 13, pero ninguno de los dos pudo leerlo.
  • Lo más interesante fue que en iOS, al mantener presionada la imagen, indica que va a github.com, pero la vista previa en sí aparece como Mastodon.
    Parece significar que el código QR se parsea dos veces y se obtienen resultados distintos. Da la impresión de que podría usarse para engañar a algunos usuarios, aunque no sé cuánta gente mira la URL del menú desplegable al mantener presionado.

    • A veces me pasa algo incómodo: el propio código QR está en la pantalla que estoy viendo en el teléfono.
      Por eso he hecho cosas tontas como enviar por AirDrop una captura de pantalla a la laptop para escanearla con la cámara del celular, o pedirle a un amigo o familiar que escanee con su teléfono el código que aparece en la pantalla del mío.
      Pero hoy me enteré de que, si mantienes presionado un código QR en una captura de pantalla dentro del carrete, lo parsea directamente y permite abrir el enlace. Creo que antes ya había probado mantener presionada una imagen, pero quizá agregaron la función después, quizá en realidad nunca lo hice, o quizá presioné una parte equivocada del código QR. En cualquier caso, me alegra mucho saber que se puede.
    • Es totalmente imaginable que dos partes del sistema operativo usen parseadores de QR distintos. SmartText podría usar uno y el sistema de imágenes otro. Parece que cada uno implementa la corrección de errores con pequeñas diferencias.
      Creo que se podría lograr lo mismo con un código QR estándar al que se le introduzcan errores deliberados. Solo habría que averiguar cómo corrige cada uno los errores de forma distinta.
      Parece que encontraste un bug de bug bounty esperando a que alguien lo reclame.
    • Los códigos QR deberían mostrar también la URL de destino como texto, para que el usuario sepa adónde va. Debería funcionar como una especie de consentimiento explícito.
    • En iOS, al mantener presionado, el enlace predeterminado “Open” aparece como Mastodon, y en el menú contextual también se muestra el enlace de la app “Open in Github”.
    • Parece el caso típico de llamar dos veces a la misma función o método getter en una situación donde ambas llamadas necesariamente deberían devolver el mismo resultado.
      Me recuerda a código como if someCondition(getFoo()) then doSomethingWith(getFoo()), o a llamar doSomethingWith(getFoo()) y luego doAnotherThingWith(getFoo()). A diferencia de tomar el valor una sola vez con foo := getFoo(), este tipo de cosas siempre deja olor a código.
  • Si haces scroll hacia arriba desde la posición inicial, puedes ver la versión lenticular.

  • Es bastante impactante. Mi iPhone tendía a quedarse fijo en uno de los dos, y girar el teléfono ayudaba a ir hacia uno u otro.
    Algunas veces los enlaces de Mastodon y GitHub parpadeaban alternándose.

  • Si quieres probarlo tú mismo, puedes usar este código hecho rápido. Instala el paquete de Python qrcode y ejecuta el código de abajo.
    En efecto genera un código QR doble con foo y bar, pero en HN Unicode no funciona, y si lo reemplazo por 8 o 0, mi teléfono no lo reconoce, así que es difícil pegarlo tal cual.

    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)  
    
  • Sería genial usarlo en una búsqueda del tesoro.

    • También se podría hacer que necesites los resultados de dos, o de todos los códigos QR, para obtener el código, enlace o llave final.
  • Los códigos QR son perfectos para abusos, igual que las URL acortadas.
    Son URLs esencialmente ilegibles, así que no tienes idea de adónde te van a mandar ni cuántas veces te van a redirigir. Por eso no los uso.

  • Es un concepto realmente genial. Si nos enfocamos en el objetivo final, también es posible otro enfoque: poner un switch en el destino de la URL.
    Sería una forma de redirigir a páginas distintas según aleatorización, datos del usuario u otros criterios. Si alguien quiere experimentar con esta función e incluso hacer pruebas con stickers reales, trabajo con varias empresas SaaS en etiquetas variables, así que puedo compartir insights, imprimir muestras o colaborar.