Wozniak y Jobs en su época de hackers

Nos hackearon, pero todo bien

Un(a) hacker se robó 0.3 ETH (~70 USD) de nuestro contrato inteligente. Cómo lo hizo, qué consecuencias tuvo, cómo vamos a evitar que se repita y qué opinamos del suceso.

Emilio S.
coinosis
Published in
7 min readJul 22, 2020

--

Lo que sucedió

Jueves 16 de julio, 7:43 pm: la segunda sesión del curso de desarrollo de dApps estaba llegando a su fin, así que, luego de una pausa de 5 minutos, exhorté a todos los participantes a que enviaran sus aplausos. Había 19 personas inscritas, así que cada quien tenía derecho a repartir 57 aplausos entre los demás participantes.

7:54 pm: faltando 6 minutos para que se terminara el evento, una nueva cuenta, 673E, se inscribió al mismo. Se trataba de una cuenta muy especial, pues era un contrato inteligente. Presumiblemente para ahorrar gas, este contrato inteligente fue creado en la misma transacción en la que se inscribió al evento. Por un bug de nuestra aplicación, 673E no apareció entre la lista de invitados, pues no estaba registrada en coinosis.

7:55 pm: súbitamente 10 nuevas cuentas se inscribieron al evento. Todas ellas eran contratos inteligentes creados por 673E. Esto hizo que el número total de participantes subiera a 30, lo cual aumentó el número de aplausos por persona a 90. No obstante, la mayoría de personas había enviado ya sus 57 aplausos, lo cual les otorgó una ventaja injusta a los recién llegados.

En una misma transacción estas 10 cuentas fueron creadas por 673E, se inscribieron al evento, y le enviaron cada una sus 90 aplausos a 673E, quien recibió así 901 aplausos. A modo de comparación, yo quedé en segundo lugar con 193 aplausos.

8:01 pm: la distribución de los fondos fue activada por la cuenta
065c, que no está registrada en coinosis. Por un bug de nuestra aplicación, la distribución no apareció en nuestro front-end, pues la misma no fue desencadenada desde coinosis. 0.942 ETH, es decir, algo más de la mitad del total de fondos que había en el contrato, se transfirieron a 673E.

Dado que todas las cuentas que participan de un evento de coinosis reciben 1 aplauso por inscribirse, nuestro contrato inteligente intentó además enviar pequeñas cantidades de ether a cada una de las 10 cuentas creadas por 673E. No obstante, estos contratos no estaban configurados para recibir dinero, así que el mismo se quedó atrapado para siempre en nuestro contrato inteligente. Éste fue probablemente el único error de nuestro(a) atacante.

8:06 pm: 673E se autodestruyó después de enviar comandos de autodestrucción a los 10 contratos creados. Todos los fondos recibidos por 673E se transfirieron a su creadora, 065c, la misma cuenta que activó la distribución de los fondos. Sin duda, ésta es la cuenta del o de la hacker.

Nuestras vulnerabilidades

La persona detrás de este ataque (en adelante, 065c) explotó una vulnerabilidad del diseño de nuestro contrato inteligente, y logró esconderse muy bien aprovechándose del hecho de que nuestro front-end no está diseñado para lidiar con participantes que estén interactuando directamente con el contrato inteligente.

Problemas de diseño de nuestro contrato inteligente

Dos características aparentemente inocuas del diseño de nuestro contrato inteligente llevaron, combinadas, a esta vulnerabilidad.

Por un lado, el número de aplausos que cada persona puede repartir depende del número total de participantes. Se trataba de una característica histórica de nuestros eventos que, inspirada en la votación cuadrática de Glenn Weyl, tenía como objetivo ilustrar el carácter finito de los aplausos disponibles. Pero en realidad no aportaba mucho y estábamos planeando eliminar esta característica en una futura versión.

Por otro lado, los participantes se pueden inscribir hasta el último momento antes de hacer la distribución. Esto, con el objetivo de respetar el inalienable derecho de las personas a llegar tarde a las cosas.

Ambas características, combinadas, le otorgan una ventaja injusta a alguien que se inscriba después de que otros hayan enviado sus aplausos, pues al inscribirse aumenta el número de participantes, y por lo tanto el número de aplausos disponibles, así que sus aplausos van a tener más peso que los aplausos de las personas que votaron temprano.

Un front-end diseñado pensando que todos lo van a usar

Todas las pruebas que habíamos hecho hasta el momento con el front-end habían sido siguiendo un flujo de ejecución de un usuario normal que utiliza únicamente la interfaz gráfica para interactuar con el contrato inteligente. Lo que no tuvimos en cuenta es que el front-end debería poder mostrar también las acciones de otros usuarios que no lo estén usando.

Cuando una persona se inscribe a un evento sin usar el front-end, lo puede hacer sin registrarse en coinosis. Esto, a su vez, implica que el front-end no encuentra el nombre del usuario y por lo tanto no lo muestra en la lista de asistentes.

Por otro lado, cuando una persona desencadena la distribución de fondos de un evento ejecutando la distribución sin usar el front-end, el mismo no registra el precio del ether al momento de la distribución, y por lo tanto no sabe cómo mostrarla.

Pérdidas y ganancias

¿Cuánto ganó 065c? ¿Cuánto perdió el resto?

Para obtener los 0.942 ETH que se robó, 065c tuvo que hacer una gran inversión. En primer lugar, tuvo que inscribir 11 cuentas al evento. Dado que el aporte costaba 0.06 ETH por persona, 065c tuvo que pagar 0.66 ETH en aportes para el evento. Es decir que, antes de costos, 065c tuvo una ganancia de 0.281 ETH. No obstante, para interactuar con nuestro contrato inteligente de la manera en que lo hizo, 065c incurrió en costos de transacción significativos, por un total de 0.128 ETH. Así que su utilidad neta es de 0.154 ETH, unos 38 dólares, y su ROI es del 19.5%.

065c se llevó la mitad de los fondos del contrato, pero una buena parte de esa suma la había puesto él mismo (o ella misma). El dinero que 065c les quitó a los demás participantes corresponde en realidad a su ganancia antes de costos, es decir, 0.281 ETH. Si 065c no se hubiera inscrito, habría habido 1.14 ETH en el contrato, así que las pérdidas de todos los demás participantes son del 24.7%. En otras palabras, sin la hazaña de 065c, todos los demás participantes habríamos obtenido 32.8% más dinero del que ganamos.

Acciones correctivas

Los nuevos eventos de coinosis, como por ejemplo la tercera sesión del curso de desarrollo de dApps que tendrá lugar mañana, tendrán un número fijo de aplausos permitidos por persona. Que el número de aplausos no dependa de el número de asistentes hace que si alguien se inscribe después de que otros hayan enviado sus aplausos, no va a tener más votos que ellos.

Por otro lado, también consideramos la posibilidad de evitar que las personas se puedan inscribir a último momento. Sin embargo, queremos hacer una hApp flexible donde la moral no se imponga sino que se incentive. Por lo tanto, con un número fijo de aplausos, las personas pueden seguir llegando tarde, pero cuanto más tarde lleguen, menos probable es que reciban aplausos.

Finalmente, el front-end está mejor equipado para lidiar con sucesos generados afuera de sí mismo. Cuando alguien se inscribe a un evento por fuera del front-end y no está inscrito a la aplicación, no aparece su nombre en la lista de invitados (pues no tiene), sino que aparece su dirección de Ethereum. Y cuando se ejecuta la distribución por fuera del front-end, el precio de conversión lo fija la primera persona que visite la distribución, lo cual sucede presumiblemente muy pronto después de que ésta se efectúe.

Disculpas

Les pedimos públicamente disculpas a todos los asistentes a nuestra segunda sesión del curso de desarrollo de dApps. Paradójicamente en un curso donde estábamos hablando de seguridad de contratos inteligentes, fuimos víctimas de una vulneración de la seguridad del nuestro. Lamentamos que su recompensa haya sido casi un tercio menor de lo esperado. Ustedes se merecen eso y mucho más.

Reflexiones finales

En coinosis tenemos una cultura hacker-friendly. Lo decíamos antes de este incidente, y lo seguimos diciendo. ¿Qué significa esto?

Significa dos cosas. Por un lado, que somos conscientes de que la única manera de garantizar la seguridad de cualquier sistema es poniéndolo a prueba; si bien en el futuro organizaremos bug bounties precisamente para premiar a quienes nos ayuden a probar la seguridad de nuestro sistema sin ponerlo en riesgo, es inevitable que nuestra app sea un bug bounty en sí misma. Y por otro lado, nos gusta la idea de que el código es ley, y que quien encuentra fallos en el código encuentra fallos en la ley y los puede explotar a su favor.

Que nos hayan hackeado muestra que nuestro proyecto es relevante. Es un halago que una persona con las capacidades intelectuales de 065c haya decidido hackearnos precisamente a nosotros. Creemos que la actitud contestataria y crítica de los hackers es una fuerza positiva en el mundo. Sin su experiencia como hackers, Jobs y Wozniak no habrían podido fundar Apple.

Así que invitamos a 065c y a todos los demás hackers que lean esto, a que sigan analizando nuestro sistema e intenten hackearlo. Lo que logren llevarse se lo merecen, porque así nos ayudan a mejorar nuestra seguridad.

--

--

Emilio S.
coinosis

“Don’t ask the chicken about the chicken soup” L. von Trier