Otra vulnerabilidad critica de dia cero en Argent-X StarkNet Billetera: la historia
TL;DR
Argent-X Billetera para StarkNet tenia otra vulnerabilidad critica, esta vez en el lado de la aplicacion. Una vez que se desbloquea la billetera, la vulnerabilidad permite que cualquier sitio web, sin ninguna interaccion del usuario, robe todos los fondos de los usuarios de todas las cuentas de Argent-X.
Este error permite que cualquier sitio web malicioso, sin pedir el consentimiento del usuario, controle completamente la billetera, lea toda la informacion de la cuenta y realice transacciones sin la aprobacion del usuario.
Esta vulnerabilidad parece existir al menos desde mayo de 2022 y todos los usuarios de Argent-X en la red principal de StarkNet estuvieron en riesgo durante todo este tiempo.
Alertamos al equipo de Argent sobre este problema hace un par de semanas y lanzaron una solucion para la tienda de Chrome en 18 horas. Desde entonces ha pasado suficiente tiempo para que la gran mayoria de usuarios obtengan la actualizacion.
Esta vulnerabilidad hace imposible distinguir entre transacciones legitimas y maliciosas, pero afortunadamente, Argent afirma que no se perdieron fondos.
En esta publicación, desentrañaremos la historia de cómo descubrimos esta vulnerabilidad, además de demostrar la importancia <-> de un mecanismo seguro de comunicación entre dApp.
Para aquellos que quieran seguir los pasos a continuación y reproducir el truco en casa, pueden descargar cualquier versión de Argent-X anterior a la 5.0.7 (por ejemplo, ésta), y ejecutar los ejemplos usted mismo.
Fondo
Como vimos hace apenas dos semanas en esta vulnerabilidad sin clic descubierta e informada por Yoav Gaziel de nuestro equipo aquí en Braavos, el contrato de la cuenta Argent-X tenía una falla de seguridad crítica. Pero ese era el codigo del contrato que tenia el error. Entonces, supongamos que el contrato Argent-X sera auditado (nuevamente) y estara libre de errores. Es eso suficiente para que una billetera se considere segura? Que pasa con el codigo de cliente de billetera? Recientemente, en Braavos decidimos probar un poco y ver.
Lo que sigue es una descripcion de aquella tarde-noche agitada.
Mensajes, Mensajes
Retrocediendo en el tiempo un ano completo: al escribir nuestra primera dApp en Starknet, utilizamos la unica billetera disponible en ese entonces: Argent-X Wallet. (Hoy puedes y debes usar Braavos).
Mientras inspeccionamos algunos mensajes enviados dentro de nuestra dApp, vimos un monton de mensajes que no enviamos y que parecen haberse originado en la propia billetera Argent-X. No pensamos mucho en eso en ese momento.
Pero recordando eso, al sentarnos esa tarde pensamos que deberiamos echar un vistazo al marco de mensajeria Argent-X. Descargar y compilar el codigo de la billetera fue facil: felicitaciones al equipo de Argent por su buen trabajo alli. Una cosa que notamos desde el principio: todavia estan usando Manifest V2. Le ahorraremos los detalles, pero digamos que Chrome Store recomienda (y pronto impondra) que todas las extensiones se trasladen a Manifest V3, que Google considera mas seguro.
Ahora vamos al unboxing del sistema de mensajeria Argent-X.
Unas pocas palabras sobre como funciona la billetera Argent-X: es una extension de navegador que se ejecuta en un entorno aislado (aislado de la pagina web en la que estas navegando). La comunicacion desde la dApp a la billetera se realiza a traves de mensajes, en un fragmento de codigo inyectado en todas y cada una de las paginas web que visita. Hasta ahora, esto es bastante estandar.
Pero al implementar un esquema de este tipo, es necesario tener muy en cuenta la seguridad.
NO QUIERES permitir que mensajes maliciosos de origenes desconocidos se infiltren en tus lineas.
Al observar el codigo de la billetera Argent-X, encontramos los tipos de eventos que utiliza con bastante rapidez:
Nos sumergimos directamente en el mensaje más intrigante — MiscenalleousMessage. No solo por el error tipografico (esta bien, nadie puede deletrear esa palabra), sino porque, segun nuestra experiencia, los desarrolladores tienden a poner las «cosas buenas» en esas clases generales.
Informando a través background/miscellaneousMessaging.ts, un mensaje específico inmediatamente llamó nuestra atención (lo dejará esperando algunas oraciones más sobre cuál). Asi que seguimos adelante y compilamos el monedero Argent-X y lo instalamos en un navegador Chrome. Inmediatamente abrio una pestana del navegador solicitando configurar la billetera y, despues del baile habitual de contrasenas, la billetera genero automaticamente una cuenta en testnet.
Luego abrimos una consola y pirateamos este codigo:
msg = { type: “RESET_ALL” }
extId = document.getElementById(«argent-x-extension»)?.getAttribute(«data-extension-id»)
window.postMessage({ …msg, extensionId: extId }, window.location.origin)
Lo que estamos haciendo aquí es enviar un evento RESET_ALL a la billetera Argent-X desde una página web no autorizada. Este tipo de hackeo deberia ser detenido por el marco de seguridad de la billetera.
Entonces ejecutamos este fragmento de codigo en la consola del navegador y abrimos la billetera Argent-X desde la barra de herramientas.
Boom 💥
La billetera olvido la configuracion que acabamos de realizar y volvio al modo de configuracion, abriendo una nueva pestana como la que se abre al registrarse.
Recapitulemos lo que acaba de suceder: enviamos un mensaje RESET_ALL desde una página web aleatoria a la billetera Argent-X y llegó hasta el final.
En este punto, comenzamos a debatir que deberamos intentar a continuacion. Al repasar el codigo fuente de los mensajes, quedo bastante claro que no existe ningun mecanismo de seguridad, aunque la billetera esta totalmente controlada por los mensajes.
Leyendo todas las cuentas
Lo siguiente que decidimos explorar fue si una pagina web no autorizada puede leer toda la informacion de su cuenta.
Para eso, no basta con enviar mensajes a la billetera. Tambien necesitaras recibir algunos mensajes. Por supuesto, una billetera bien protegida no deberia enviar este tipo de informacion a una pagina web no autorizada.
Al observar el código en background/accountMessaging.ts, vimos este mensaje GET_ACCOUNTS que planeábamos usar. Lo que nos llamó la atención es la llamada a sendToTabAndUi() con el resultado GET_ACCOUNTS_RES Nos dimos cuenta de que la interfaz de usuario es la interfaz de usuario de Argent-X que necesita obtener la informacion de la cuenta, lo mas probable es que muestre la interfaz de usuario al usuario mientras navega por la ventana emergente de Argent-X. Pero por que enviar esto a la pestana? Esto parece un fallo de seguridad.
Reinstalamos una billetera Argent-X nueva y luego pirateamos este fragmento de codigo en la consola:
function imprimir_evento(e) {
console.log(e)
}
window.addEventListener(«mensaje», imprimir_evento)
msg = { type: “GET_ACCOUNTS” }
extId = document.getElementById(«argent-x-extension»)?.getAttribute(«data-extension-id»)
window.postMessage({ …msg, extensionId: extId }, window.location.origin)
Aún otra Boom 💥 :
El resultado de este flujo es que cualquier sitio web (de noticias, gubernamental, etc.) puede leer todas sus direcciones de Argent-X. Pero espera, la billetera estaba desbloqueada. Quizas una billetera cerrada evitaria este defecto? Bloqueamos la billetera desde la interfaz de usuario y lo intentamos nuevamente.
Mismo resultado. Incluso una billetera bloqueada devuelve, a cualquier sitio web, la lista de todas sus cuentas.
Al examinar el manifiesto de Argent-X, vimos que inyectan su codigo de extension en cualquier sitio web. http, https, local… Cualquiera de ellos podria hacer precisamente esto.
El Santo Grial: ejecutar transacciones
Acortaremos la historia aqui y describiremos brevemente como se desarrollaron las siguientes horas.
Mirando en el lugar natural: el archivo background/transactions/transactionMessaging.ts,
el primer mensaje que hay EXECUTE_TRANSACTION.
Este es el santo grial.
Nos tomo unos minutos comprender el formato de transaccion requerido, pero muy pronto lo tuvimos:
msg = { type: “EXECUTE_TRANSACTION”, data:{transactions:{contractAddress:”0x049D36570D4e46f48e99674bd3fcc84644DdD6b96F7C741B1562B82f9e004dC7″,entrypoint:”transfer”,calldata:[“0x07074236724072a4949b7B170E5D5825b8195Ec278ED45E4BE9975186758EDa7”,”10000000000″,”0″]}} }
Este mensaje solicita una transferencia de 1e-9 ETH a alguna direccion arbitraria (nuestra propia direccion en este caso. Eramos tacanos, esos Goerli ETH son dificiles de conseguir!).
Enviamos este mensaje de la forma habitual, pero adivina qué… aunque recibimos una respuesta EXECUTE_TRANSACTION_RES de la billetera, no pareció realizarse ninguna transacción.
Al abrir la interfaz de usuario de la cartera, comprendimos inmediatamente por que.
Como puede verse, la billetera acepto el mensaje pero aun le pide al usuario que confirme esta transaccion. Esta linea de defensa impedira que un hacker realice esta transaccion?
Al observar la respuesta que envió la billetera, parece que hay algún parámetro llamado actionHash.
Esta actionHash debe representar algo.
Haciendo lo natural, nos sumergimos en background/actionMessaging.ts, y allí vimos este mensaje:
Llamamos a este mensaje, con el actionHash, anterior, y…
💥 BOOM BOOM BOOM 💥
Ves esta insignia ‘1’?
Esa es la transaccion de transferencia de ETH no autorizada y no solicitada que hemos generado.
Tenga en cuenta que este flujo es posible cuando el usuario navega por la web y la billetera esta desbloqueada. Una vez que se desbloquea la billetera, esta permanece desbloqueada durante mucho tiempo, a menudo hasta que el usuario cierra el navegador.
Poniendolo todo junto
Después de respirar profundamente, nos sentamos durante una hora para piratear un pequeño POC (una página de JavaScript que hace todo lo que acabamos de describir, con una pizca en la parte superior) usando el mensaje CONNECT_ACCOUNT para cambiar a cualquier cuenta en la billetera (incluidas las cuentas de la red principal).
Ya era bastante tarde, pero nos dimos cuenta de que no podiamos sentarnos en esto. Nos pusimos en contacto con nuestros becarios de Braavos, les informamos y les pedimos que probaran el POC. Se sorprendieron pero siguieron adelante. Y funciono en todas sus maquinas.
Mientras uno de nosotros pirateaba el POC, el otro navegaba por el historial de git de la billetera Argent-X, tratando de estimar cuanto tiempo ha estado sucediendo esto, y parece que ha estado sucediendo desde el primer dia. Todas las billeteras Argent-X han sido asi de vulnerables al menos desde mayo, cuando Argent-X introdujo el soporte para la red principal StarkNet.
A partir de ahora, seguimos el protocolo de Divulgacion Responsable, tal como lo hicimos apenas una semana antes cuando expusimos el error del contrato de clic cero al mismo equipo de Argent.
Aunque esta exploracion hizo que nos perdieramos el fantastico partido entre Espana y Costa Rica (7-0!! Que??) y el partido entre Belgica y Canada (1-0).
Estamos felices de haber podido ayudar al ecosistema StarkNet y garantizar que los fondos de los usuarios ya no esten en peligro.