Vulnerabilidades en Etherium Smart Contracts. Ejemplos de c贸digo

Con esta publicaci贸n, comienzo una serie de art铆culos sobre la seguridad de los contratos inteligentes de Ethereum. Creo que este tema es muy relevante, ya que el n煤mero de desarrolladores est谩 creciendo como una avalancha, y no hay nadie a quien salvar del "rastrillo". Adi贸s - traducciones ...

1. Escaneo de contratos de Ethereum en vivo para detectar errores de env铆o sin marcar


Original: escaneo de contratos de Ethereum en vivo para "Enviar sin marcar ..."


Autores: Zikai Alex Wen y Andrew Miller

Se sabe que la programaci贸n de contratos inteligentes de Ethereum es propensa a errores [1] . Recientemente, vimos que varios
Los contratos inteligentes de alta gama como King of the Ether y The DAO-1.0 conten铆an vulnerabilidades causadas por errores de programaci贸n.

Desde marzo de 2015, los programadores de contratos inteligentes han sido advertidos de los peligros de programaci贸n espec铆ficos que pueden surgir cuando los contratos se env铆an mensajes entre s铆 [6] .

Varias gu铆as de programaci贸n recomiendan c贸mo evitar errores comunes (en documentos t茅cnicos Ethereum [3] y en una gu铆a independiente de UMD [2] ). Aunque estos peligros son lo suficientemente comprensibles para evitarlos, las consecuencias de tal error son terribles: el dinero puede ser bloqueado, perdido o robado.

驴Qu茅 tan comunes son los errores resultantes de estos peligros? 驴Hay alg煤n contrato de blockchain de Ethereum m谩s vulnerable pero vivo? En este art铆culo, respondemos esta pregunta analizando los contratos en la cadena de bloques en vivo de Ethereum utilizando la nueva herramienta de an谩lisis que desarrollamos.


驴Cu谩l es el error de env铆o sin marcar?


Para enviar un contrato de tiempo aire a otra direcci贸n, la forma m谩s f谩cil es usar la palabra clave enviar . Esto act煤a como un m茅todo definido para cada objeto. Por ejemplo, el siguiente fragmento de c贸digo se puede encontrar en un contrato inteligente que implementa un juego de mesa.


/*** Listing 1 ***/ if (gameHasEnded && !( prizePaidOut ) ) { winner.send(1000); //    prizePaidOut = True; } 

El problema aqu铆 es que el m茅todo de env铆o puede fallar. Si no funciona, el ganador no recibir谩 el dinero, sin embargo, el premio variablePaidOut se establecer谩 en True.

Hay dos casos diferentes en los que la funci贸n winner.send () puede fallar. Analizaremos la diferencia entre ellos m谩s adelante. El primer caso es que la direcci贸n del ganador es un contrato (no una cuenta de usuario), y el c贸digo para este contrato arroja una excepci贸n (por ejemplo, si usa demasiado "gas"). Si es as铆, entonces quiz谩s en este caso sea un "error del ganador". El segundo caso es menos obvio. La m谩quina virtual Ethereum tiene un recurso limitado llamado " callstack " (profundidad de la pila de llamadas), y este recurso puede ser utilizado por otro c贸digo de contrato que se ejecut贸 previamente en una transacci贸n. Si la pila de llamadas ya se ha agotado cuando se ejecuta el comando de env铆o , el comando fallar谩, independientemente de c贸mo se determine el ganador . 隆El premio del ganador ser谩 destruido sin culpa suya!



驴C贸mo se puede evitar este error?

La documentaci贸n de Ethereum contiene una breve advertencia sobre este peligro potencial [3] : "Existe alg煤n peligro cuando se utiliza el env铆o: la transmisi贸n falla si la profundidad de la pila de llamadas es 1024 (esto siempre puede ser causado por la persona que llama), y tambi茅n falla si el destinatario "gas" termina. Por lo tanto, para garantizar una transmisi贸n segura, siempre verifique el valor de retorno del env铆o, o incluso mejor: use una plantilla en la que el destinatario retire dinero ".

Dos oraciones El primero es verificar el valor de retorno de enviar para ver si se complet贸 con 茅xito. Si este no es el caso, lanza una excepci贸n para revertir el estado.


  /*** Listing 2 ***/ if (gameHasEnded && !( prizePaidOut ) ) { if (winner.send(1000)) prizePaidOut = True; else throw; } 

Esta es una soluci贸n adecuada para el ejemplo actual, pero no siempre es la decisi贸n correcta. Supongamos que modificamos nuestro ejemplo para que cuando el juego termine, el ganador y el perdedor retrocedan sus fortunas. Una aplicaci贸n obvia de una soluci贸n "formal" ser铆a la siguiente:


 /*** Listing 3 ***/ if (gameHasEnded && !( prizePaidOut ) ) { if (winner.send(1000) && loser.send(10)) prizePaidOut = True; else throw; } 

Sin embargo, esto es un error porque introduce una vulnerabilidad adicional. Si bien este c贸digo protege al ganador de un ataque de pila de llamadas , tambi茅n hace que el ganador y el perdedor sean vulnerables entre s铆. En este caso, queremos evitar un ataque de pila de llamadas , pero continuar la ejecuci贸n si el comando de env铆o falla por alguna raz贸n.

Por lo tanto, incluso la mejor pr谩ctica recomendada (recomendada en nuestra Gu铆a del programador de Ethereum y Serpent, aunque se aplica igualmente a Solidity), es verificar si hay un recurso de pila de llamadas . Podemos definir una macro callStackIsEmpty () , que devolver谩 un error si y solo si callstack est谩 vac铆o.


 /*** Listing 4 ***/ if (gameHasEnded && !( prizePaidOut ) ) { if (callStackIsEmpty()) throw; winner.send(1000) loser.send(10) prizePaidOut = True; } 

A煤n mejor, la recomendaci贸n de la documentaci贸n de Ethereum: "Usar una plantilla en la que el destinatario toma el dinero" es un poco cr铆ptica, pero tiene una explicaci贸n. La sugerencia es reorganizar su c贸digo para que el efecto del error de env铆o est茅 aislado y afecte a un solo destinatario a la vez. El siguiente es un ejemplo de este enfoque. Sin embargo, este consejo tambi茅n es un antipatr贸n. 脡l acepta la responsabilidad de verificar la pila de llamadas a los propios destinatarios, lo que hace posible caer en la misma trampa.


 /*** Listing 5 ***/ if (gameHasEnded && !( prizePaidOut ) ) { accounts[winner] += 1000 accounts[loser] += 10 prizePaidOut = True; } ... function withdraw(amount) { if (accounts[msg.sender] >= amount) { msg.sender.send(amount); accounts[msg.sender] -= amount; } } 

Muchos contratos inteligentes altamente desarrollados son vulnerables. La loter铆a del Rey del Aire del Trono es el caso m谩s famoso de este error [4] . Este error no se not贸 hasta que la cantidad de 200 茅teres (con un valor de m谩s de 2000 d贸lares estadounidenses al precio actual) no pudo obtener el ganador leg铆timo de la loter铆a. El c贸digo correspondiente en King of the Ether es similar al c贸digo del Listado 2. Afortunadamente, en este caso, el desarrollador del contrato pudo usar la funci贸n no relacionada en el contrato como una "anulaci贸n manual" para liberar los fondos atascados. 隆Un administrador menos escrupuloso podr铆a usar la misma funci贸n para robar la transmisi贸n!


Contin煤a escaneando contratos de Ethereum en vivo para detectar errores de env铆o sin marcar. Parte 2

Source: https://habr.com/ru/post/es430602/


All Articles