Resumen
El fallo no está en una línea de código concreta, sino en cómo funciona el proceso de gasto de los monederos multifirma (multisig) de Monero, descrito en la documentación oficial:
https://docs.getmonero.org/multisignature/#spending
En ese proceso, cada participante firma por turnos una transacción parcialmente firmada. La propia documentación advierte de que la firma puede fallar si los datos multisig han caducado, con este mensaje:
Error: Multisig error: This signature was made with stale data: export fresh multisig data, which other participants must then use
Ese error es la coartada perfecta para un participante malicioso. El escenario es este:
- El grupo quiere pagar a la dirección X y el monedero tiene dos entradas disponibles, A y B.
- Se construye una transacción que gasta A y paga a X. Todos firman, pero el último firmante es el atacante.
- El atacante completa la firma, se guarda la transacción sin difundirla y dice al resto que le ha dado el error de datos obsoletos: «hay que repetirla».
- Se construye una nueva transacción, esta vez con la entrada B, también hacia X. El grupo la firma y la difunde.
- El atacante difunde entonces la primera (A → X). X cobra dos veces.
El incentivo es evidente si el propio atacante controla X.
Según el investigador, lo que hace el ataque indetectable en la práctica son tres limitaciones del software de Monero en el momento del reporte:
- No se pueden ver las entradas de una transacción multisig desde ningún comando del monedero ni RPC. Si se pudieran ver, el resto de participantes se negaría a firmar B → X después de haberse comprometido con A → X.
- No se pueden elegir las entradas al construir una transacción: ni
transferni otro comando lo permiten. Así, aunque se vieran las entradas, un participante honesto que sufriera de verdad el error de datos obsoletos podría acabar con una transacción nueva que usa entradas distintas (por ejemplo, si el multisig ha recibido fondos entretanto) y parecería malicioso. El atacante, por su parte, puede enviar fondos al multisig a propósito hasta que el algoritmo de selección escoja otras entradas. - Las firmas caducan, de modo que alegar «datos obsoletos» resulta creíble.
El investigador compara con Bitcoin, donde el problema no existe porque las entradas se pueden ver y elegir, y las firmas no caducan. También explica que antes de reportarlo lo consultó en el canal de investigación de Monero, donde le confirmaron que todavía no había forma de evitarlo.
A raíz de esa conversación, jeffro256 preparó una rama para poder ver las entradas de la transacción:
https://github.com/monero-project/monero/pull/10281
Afecta a todas las versiones.
Pasos de reproducción
- Crea con la CLI un monedero multisig en una red local de pruebas.
- Empieza a construir una transacción multisig y comprueba que, aunque no se produzcan bloques, tras esperar unos minutos aparece al firmar el error
Error: Multisig error: This signature was made with stale data: export fresh multisig data, which other participants must then use, tal como indica la documentación. - Construye una transacción.
- Fírmala con todos los participantes. El último se la queda y no la difunde.
- Envía más salidas al monedero multisig hasta que
transferconstruya una transacción con una entrada distinta de la anterior (el investigador reconoce que no está claro qué criterio lo determina). La diferencia de entradas puede comprobarse con los cambios de la PR 10281. - Firma y difunde esta segunda transacción.
- Difunde la primera.
Impacto
Pérdida de fondos del multisig: un solo participante malicioso consigue que el grupo envíe el mismo pago varias veces cuando solo se quería hacer uno, sin que los demás puedan distinguir su comportamiento del de un participante honesto que ha sufrido el error de datos obsoletos.
Remediación
El reporte no detalla cómo se resolvió. La única medida que menciona es la PR 10281, que permite ver las entradas de una transacción; según el propio investigador, eso mitiga el problema pero no lo elimina mientras no se puedan elegir las entradas al construir la transacción.