El informe de incidentes de Scroll de esta noche tiene mucho más interés que esas cuatro palabras: «no fue explotado».
Fui a buscar la publicación original en el foro (11/11 14:24 UTC). El problema estaba en el contrato de verificación de pruebas del bundle de la mainnet, ZkEvmVerifierPostFeynman (0x7966…B254). Construía la entrada manualmente y usaba raw staticcall para llamar al verificador Halo2 de OpenVM 2.0; los primeros 4 bytes de la llamada se tomaban directamente de la proof enviada por el solicitante. Justo en OpenVM 2.0 había una función verify tipada cuyo selector era 0x24270d54. Si la proof empezaba con esos 4 bytes, en lugar de pasar por fallback entraba en esa función, que leía las entradas públicas de una ubicación controlada por quien llamaba. Resultado: una prueba válida antigua podía usarse para respaldar una nueva raíz de estado y una nueva raíz de retiros. Dicho en cristiano: en teoría, se podía confirmar una raíz de retiros inventada de la nada; el dinero del puente era el blanco.
La ironía está aquí. Este contrato se desplegó durante la actualización a OpenVM v2.0 del 22 de septiembre. Uno de los motivos de la actualización que se mencionaban en el anuncio era corregir el aviso de seguridad de la v1.7.0, una de cuyas vulnerabilidades se llamaba «OpenVmHalo2Verifier self-call can bypass proof verification»; también era una evasión en la capa wrapper del verificador. Aquella vez se calificó como una actualización de mantenimiento y solo se publicó un anuncio, sin votación. Corrigieron un fallo en la capa wrapper, pero el nuevo contrato abrió otro en esa misma capa y estuvo expuesto durante 49 días (del 9/22 al 11/10).
¿Por qué no pasó nada? El propio informe lo explica: para explotar la vulnerabilidad hacía falta el rol de prover autorizado, o bien el modo de lotes forzados, que solo se habilita después de 7 días sin confirmación final. Durante el tiempo que el contrato vulnerable estuvo en línea, las 748 confirmaciones finales usaron pruebas normales y el modo forzado nunca se activó.
Así que lo que realmente protegió durante esos 49 días no fueron las matemáticas ZK, sino que «solo los provers de Scroll podían enviar pruebas». A menudo se critica la centralización de los provers de las L2; esta vez, precisamente esa centralización mantuvo la puerta cerrada. Y lo más doloroso es verlo al revés: la cápsula de escape de 7 días, diseñada para resistir la censura, se convierte, si se activa, en una vía de ataque que cualquiera puede usar.
Le doy una buena nota a la divulgación. El white hat ZHOUCHUNGONG informó del fallo el 11/3; el 11/10 se registró el contrato de corrección. Publicaron la nueva dirección, el PR de corrección y la transacción de registro, además de los pasos para que cualquiera pudiera verificarlo por su cuenta. Mejor que muchos proyectos. Pero el discurso de «si hay una validity proof, no hace falta confiar en el operador» acaba de ser desmontado por el propio informe del proyecto. Quienes tengan activos en una L2 no deberían preguntar solo si es ZK: también deberían preguntar quién ejecuta el prover, cada cuánto se cambia el contrato de verificación y si hay votación y auditoría cuando se cambia. El informe no indica cuánto fue la recompensa ni si el nuevo contrato tuvo una auditoría externa; no voy a inventármelo.
#Scroll #EthereumL2
Fui a buscar la publicación original en el foro (11/11 14:24 UTC). El problema estaba en el contrato de verificación de pruebas del bundle de la mainnet, ZkEvmVerifierPostFeynman (0x7966…B254). Construía la entrada manualmente y usaba raw staticcall para llamar al verificador Halo2 de OpenVM 2.0; los primeros 4 bytes de la llamada se tomaban directamente de la proof enviada por el solicitante. Justo en OpenVM 2.0 había una función verify tipada cuyo selector era 0x24270d54. Si la proof empezaba con esos 4 bytes, en lugar de pasar por fallback entraba en esa función, que leía las entradas públicas de una ubicación controlada por quien llamaba. Resultado: una prueba válida antigua podía usarse para respaldar una nueva raíz de estado y una nueva raíz de retiros. Dicho en cristiano: en teoría, se podía confirmar una raíz de retiros inventada de la nada; el dinero del puente era el blanco.
La ironía está aquí. Este contrato se desplegó durante la actualización a OpenVM v2.0 del 22 de septiembre. Uno de los motivos de la actualización que se mencionaban en el anuncio era corregir el aviso de seguridad de la v1.7.0, una de cuyas vulnerabilidades se llamaba «OpenVmHalo2Verifier self-call can bypass proof verification»; también era una evasión en la capa wrapper del verificador. Aquella vez se calificó como una actualización de mantenimiento y solo se publicó un anuncio, sin votación. Corrigieron un fallo en la capa wrapper, pero el nuevo contrato abrió otro en esa misma capa y estuvo expuesto durante 49 días (del 9/22 al 11/10).
¿Por qué no pasó nada? El propio informe lo explica: para explotar la vulnerabilidad hacía falta el rol de prover autorizado, o bien el modo de lotes forzados, que solo se habilita después de 7 días sin confirmación final. Durante el tiempo que el contrato vulnerable estuvo en línea, las 748 confirmaciones finales usaron pruebas normales y el modo forzado nunca se activó.
Así que lo que realmente protegió durante esos 49 días no fueron las matemáticas ZK, sino que «solo los provers de Scroll podían enviar pruebas». A menudo se critica la centralización de los provers de las L2; esta vez, precisamente esa centralización mantuvo la puerta cerrada. Y lo más doloroso es verlo al revés: la cápsula de escape de 7 días, diseñada para resistir la censura, se convierte, si se activa, en una vía de ataque que cualquiera puede usar.
Le doy una buena nota a la divulgación. El white hat ZHOUCHUNGONG informó del fallo el 11/3; el 11/10 se registró el contrato de corrección. Publicaron la nueva dirección, el PR de corrección y la transacción de registro, además de los pasos para que cualquiera pudiera verificarlo por su cuenta. Mejor que muchos proyectos. Pero el discurso de «si hay una validity proof, no hace falta confiar en el operador» acaba de ser desmontado por el propio informe del proyecto. Quienes tengan activos en una L2 no deberían preguntar solo si es ZK: también deberían preguntar quién ejecuta el prover, cada cuánto se cambia el contrato de verificación y si hay votación y auditoría cuando se cambia. El informe no indica cuánto fue la recompensa ni si el nuevo contrato tuvo una auditoría externa; no voy a inventármelo.
#Scroll #EthereumL2