Lista de vulnerabilidades comunes
Las vulnerabilidades de los contratos inteligentes pueden tener consecuencias catastróficas y a menudo irreversibles una vez desplegados. La explotación de estas debilidades representa un desafío importante para la seguridad de la blockchain, resultando en miles de millones de dólares en activos robados y socavando la confianza de los usuarios. Lograr una seguridad robusta en los contratos inteligentes es, por lo tanto, primordial. Requiere más que simplemente desplegar código en la blockchain. Exige prácticas rigurosas de codificación segura, pruebas exhaustivas y, a menudo, auditorías independientes de contratos inteligentes para identificar problemas antes de que puedan ser explotados. Comprender los errores comunes –desde ataques de reentrada como el famoso hackeo de The DAO hasta errores lógicos sutiles y vulnerabilidades de Ethereum como los desbordamientos de enteros– es el primer paso crítico hacia la mitigación. Esta página proporciona una visión general esencial de las vulnerabilidades frecuentes de los contratos inteligentes para aumentar la concienciación y promover un desarrollo más seguro en el ecosistema descentralizado.
- Reentrancy
- Unexpected Ether
- DoS
- Overflow
- tx.origin Authentication
- Access Control
- Weak Randomness
- Hash Collision
- Precision Loss
- msg.value in loop
- ecrecover
- Replay
- Inheritance Order
- MEV
La reentrada es una de las vulnerabilidades más tempranas y devastadoras de los contratos inteligentes, responsable de exploits históricos significativos como el hackeo de The DAO, que condujo a un hard fork de Ethereum. Sigue siendo una amenaza crítica con consecuencias potencialmente terribles, capaz de vaciar los contratos de todos sus fondos.
La vulnerabilidad surge de un anti-patrón de codificación común, a menudo arrastrado de hábitos de la Web2: realizar interacciones externas (como transferir Ether o llamar a otro contrato) antes de actualizar el estado interno del contrato (por ejemplo, los saldos de los usuarios). Cuando un contrato envía Ether o llama a un contrato externo, transfiere temporalmente el control de la ejecución. Un atacante que controla el contrato receptor puede explotar esto realizando inmediatamente una llamada de vuelta al contrato original, a menudo a través de la función receive o fallback desencadenada por la transferencia de Ether, o mediante hooks de los estándares de tokens.
Debido a que el estado del contrato original (por ejemplo, el saldo del atacante) aún no se ha actualizado, la llamada reentrante supera las comprobaciones iniciales, permitiendo al atacante repetir la acción (como retirar fondos) múltiples veces dentro de la misma transacción. Este bucle recursivo continúa hasta que los fondos se agotan o se alcanzan los límites de gas.
Aunque el ataque clásico involucra una única función, existen variaciones más complejas, como la reentrada entre funciones (explotando el estado compartido entre funciones) y la reentrada de solo lectura (manipulando las lecturas de estado a través de funciones de visualización - view functions).
La defensa principal es el patrón Checks-Effects-Interactions (CEI): realizar todas las comprobaciones necesarias, luego actualizar las variables de estado (efectos) y solo después de estas actualizaciones, interactuar con contratos externos.
La vulnerabilidad de "Manipulación del balance mediante recepción inesperada de Ether" surge porque los contratos inteligentes de Solidity pueden recibir Ether a través de mecanismos que eluden sus funciones `receive` y `fallback` definidas. Los métodos principales para esta inyección "inesperada" de Ether son el opcode `selfdestruct`, que transfiere forzosamente el saldo de un contrato que se está destruyendo a una dirección designada, y las transacciones coinbase (recompensas de bloque).
El problema central es que estas transferencias forzadas aumentan directamente el saldo de Ether del contrato (`address(this).balance`) sin activar la lógica programada del contrato para manejar los fondos entrantes. Esto rompe una suposición o invariante común, a menudo implícita: que el saldo real del contrato refleja con precisión la suma de los fondos procesados a través de sus funciones `payable` previstas o contabilizados internamente.
Los contratos que utilizan incorrectamente `address(this).balance` para verificaciones lógicas críticas se vuelven vulnerables. Por ejemplo, los contratos podrían verificar que `address(this).balance == cantidadEsperada`. Un atacante puede explotar esto usando `selfdestruct` para enviar una pequeña cantidad de Ether, manipulando así el saldo. Esto puede llevar a:
Denegación de Servicio (DoS): Las comprobaciones de igualdad estricta pueden quedar inutilizadas permanentemente, dejando las funciones inutilizables.
Manipulación de la lógica: Los umbrales pueden alcanzarse prematuramente, desencadenando cambios de estado o pagos no intencionados.
Violaciones de `assert`: En casos raros, el saldo inesperado podría llevar a inconsistencias internas del estado que provoquen que un `assert()` falle, consumiendo todo el gas de la transacción.
La mitigación fundamental es nunca depender de `address(this).balance` para la lógica del contrato. En su lugar, los contratos deben mantener una contabilidad interna precisa utilizando variables de estado dedicadas, actualizándolas solo dentro de funciones legítimas de manejo de fondos. Todas las verificaciones críticas y transiciones de estado deben basarse en estas variables internas confiables.
Las vulnerabilidades de Denegación de Servicio (Denial of Service, DoS) en los contratos inteligentes de Solidity tienen como objetivo interrumpir o detener por completo la funcionalidad prevista del contrato, impidiendo a los usuarios legítimos acceder a sus servicios. El "servicio" que se deniega es la capacidad del contrato para ejecutar sus funciones tal como está programado y esperan sus usuarios.
Los atacantes logran esto explotando fallos en la lógica del código del contrato, manipulando las limitaciones de recursos (principalmente el gas) o aprovechando dependencias externas.
Los patrones comunes incluyen:
Agotamiento de Gas (Gas Exhaustion): Crear transacciones o manipular el estado para hacer que los costos de ejecución de la función excedan el límite de gas del bloque o el límite de gas de la transacción. Esto a menudo implica desencadenar operaciones computacionalmente costosas o, muy comúnmente, iterar sobre arrays (arreglos) no acotados cuyo tamaño puede crecer indefinidamente. A medida que los datos crecen, el costo de gas del bucle supera los límites, haciendo que la función sea inutilizable.
Reversiones Inesperadas (Unexpected Reverts): Provocar que funciones críticas reviertan (revert) inesperadamente. Esto puede ser desencadenado forzando llamadas externas fallidas (por ejemplo, enviando fondos a un contrato que los rechaza), manipulando el estado para que fallen las condiciones `require`, u otras excepciones no gestionadas. Si un contrato depende de una llamada externa que falla (quizás inducida maliciosamente), toda la operación puede ser bloqueada.
La naturaleza inmutable de los contratos inteligentes hace que el DoS sea particularmente grave. Un ataque exitoso puede llevar a fondos bloqueados permanentemente o a una funcionalidad irrecuperable, ya que el código del contrato no puede corregirse fácilmente después del despliegue.
La mitigación se basa en prácticas de codificación seguras: usar operaciones acotadas en lugar de bucles no acotados, preferir patrones de "pull-payment" (pago por solicitud/extracción) en lugar de empujar fondos a los usuarios ("push-payment"), lo que aísla los fallos de transferencia, gestionar robustamente los fallos de llamadas externas (por ejemplo, usando `try/catch` donde sea apropiado) y una gestión cuidadosa del gas.
Las vulnerabilidades de desbordamiento (overflow) y subdesbordamiento (underflow) de enteros representan una amenaza persistente en el dominio de la seguridad de los contratos inteligentes (smart contracts), derivadas de las limitaciones fundamentales de la aritmética de enteros de tamaño fijo. Un desbordamiento ocurre cuando el resultado de una operación aritmética excede el valor máximo para su tipo, y un subdesbordamiento ocurre cuando el resultado cae por debajo del valor mínimo.
Históricamente, en las versiones de Solidity anteriores a la 0.8.0, estos errores aritméticos causaban que el valor "diera la vuelta" (wrap around) silenciosamente. Por ejemplo, sumar 1 al valor máximo de uint8 (255) resultaba en 0, y restar 1 de 0 resultaba en 255. Este comportamiento predecible pero no verificado era una fuente importante de exploits, permitiendo a los atacantes manipular saldos de tokens (por ejemplo, acuñar enormes cantidades o drenar fondos haciendo que los saldos dieran la vuelta), eludir controles de seguridad críticos, corromper el estado del contrato o causar denegación de servicio (denial-of-service).
La versión 0.8.0 de Solidity introdujo una mejora de seguridad crucial: las operaciones aritméticas estándar (+, -, *, /) ahora realizan comprobaciones de desbordamiento y subdesbordamiento por defecto. Si una operación resultara en un "wrap around", la transacción se revierte (reverts) en su lugar, previniendo la corrupción silenciosa del estado.
Sin embargo, el riesgo no se elimina por completo. Los desarrolladores pueden eludir explícitamente estas comprobaciones por defecto utilizando bloques `unchecked`, a menudo para la optimización del gas. El código dentro de un bloque `unchecked` vuelve al peligroso comportamiento de "wrap around" silencioso de las versiones anteriores a 0.8.0 y requiere una validación extremadamente cuidadosa. Del mismo modo, el código ensamblador (assembly) de bajo nivel de EVM no se beneficia de las comprobaciones de Solidity, exigiendo una gestión manual de la seguridad para las operaciones aritméticas. Además, las operaciones de desplazamiento (shift) (<<, >>) permanecen sin comprobar incluso en el modo por defecto >=0.8.0 y truncarán los resultados. La conversión de tipos (type casting) incorrecta (por ejemplo, convertir un uint256 grande a un tipo más pequeño como uint8) también puede llevar al truncamiento del valor, causando potencialmente un comportamiento inesperado en cálculos posteriores.
Por lo tanto, aunque mitigado significativamente por defecto en el Solidity moderno, el desbordamiento/subdesbordamiento de enteros sigue siendo una vulnerabilidad relevante, especialmente en contratos heredados (legacy contracts), código que utiliza bloques `unchecked` o ensamblador, y a través de operaciones específicas no comprobadas o conversiones de tipos descuidadas.
El uso de `tx.origin` para la lógica de autorización en los contratos inteligentes (smart contracts) de Solidity representa una vulnerabilidad de seguridad crítica. Se deriva de una mala interpretación fundamental de su comportamiento en comparación con `msg.sender`. Mientras que `tx.origin` identifica consistentemente la EOA (Externally Owned Account / Cuenta de Propiedad Externa) que inició la transacción, `msg.sender` identifica a quien llama inmediatamente (el llamador inmediato). Usar `tx.origin` para las comprobaciones de permisos no logra validar la entidad que interactúa directamente con el contrato.
Esta falla abre la puerta a ataques de tipo phishing, donde un contrato intermediario malicioso, invocado por un usuario con privilegios, puede llamar con éxito a funciones protegidas en el contrato objetivo. La comprobación de `tx.origin` valida incorrectamente al usuario original, permitiendo que el contrato malicioso ejecute acciones con la autoridad del usuario. Las consecuencias van desde el robo directo de Ether y tokens hasta la manipulación no autorizada del estado del contrato, causando potencialmente pérdidas financieras significativas y un daño irreparable a la integridad y reputación del protocolo.
El Control de Acceso Insuficiente es una vulnerabilidad crítica en los contratos inteligentes de Solidity donde las restricciones sobre quién puede ejecutar qué funciones faltan o están implementadas incorrectamente. Dado que Solidity carece de modelos de permisos incorporados, los desarrolladores deben añadir manualmente comprobaciones, a menudo utilizando modificadores como onlyOwner o implementando Control de Acceso Basado en Roles (RBAC). No hacerlo correctamente crea agujeros de seguridad.
Esta vulnerabilidad generalmente se manifiesta como funciones desprotegidas. Funciones destinadas a operaciones administrativas o sensibles - como transferir la propiedad del contrato (changeOwner), retirar fondos (withdraw), pausar el contrato, acuñar tokens o incluso destruir el contrato (selfdestruct) - podrían dejarse disponibles para ser llamadas por cualquier cuenta externa. Esto ocurre a menudo debido a la falta de modificadores de control de acceso o a una visibilidad de función incorrecta (por ejemplo, las funciones son public por defecto si no se especifica). Las funciones de inicialización expuestas, que solo deberían ejecutarse una vez, también pueden ser un vector de ataque si permanecen accesibles después del despliegue, permitiendo potencialmente a los atacantes restablecer la propiedad o parámetros críticos.
Las consecuencias son graves, y van desde que usuarios no autorizados obtengan privilegios administrativos hasta el robo completo de los fondos gestionados por el contrato o la destrucción irreversible del propio contrato. Exploits del mundo real, como los incidentes de Parity Wallet y el hackeo de LAND Token, demuestran el potencial devastador del control de acceso insuficiente.
La mitigación implica aplicar rigurosamente comprobaciones de control de acceso a todas las funciones sensibles, adherirse al Principio de Mínimo Privilegio, usar patrones establecidos como Ownable o RBAC (a menudo a través de bibliotecas como las de OpenZeppelin) y realizar pruebas y auditorías exhaustivas.
Generar aleatoriedad segura en la Máquina Virtual de Ethereum (EVM) es un desafío debido a su naturaleza determinista, que es esencial para el consenso de la red. Esto crea una "ilusión de entropía" donde valores aparentemente aleatorios derivados puramente de datos on-chain son en realidad predecibles.
Los desarrolladores a menudo usan incorrectamente variables de bloque fácilmente disponibles como block.timestamp, blockhash y el prevrandao posterior a la Fusión (accesible a través de block.difficulty) como fuentes de pseudoaleatoriedad. Estas variables son inseguras porque pueden predecirse o ser influenciadas.
Los mineros (en Proof-of-Work) o los validadores (en Proof-of-Stake) pueden manipular estos valores hasta cierto punto para obtener una ventaja injusta, a menudo como parte de estrategias MEV (Valor Máximo Extraíble). Fundamentalmente, incluso los usuarios regulares o los contratos atacantes a menudo pueden predecir los resultados si la lógica de aleatoriedad se basa únicamente en entradas conocidas antes o durante la ejecución de la transacción, lo que permite exploits como el front-running. Esta previsibilidad socava la equidad de aplicaciones como loterías, juegos y mints de NFT.
La colisión de hash mediante `abi.encodePacked` con múltiples tipos dinámicos no surge de debilidades en la función de hash subyacente Keccak-256, sino de la forma en que se codifican los datos antes de aplicarles el hash, específicamente al usar la función `abi.encodePacked` de Solidity. A diferencia del estándar `abi.encode`, que rellena (padding) los argumentos a 32 bytes e incluye prefijos de longitud para tipos dinámicos, `abi.encodePacked` crea una codificación compacta y no estándar concatenando argumentos usando el mínimo de bytes requeridos, omitiendo el relleno para tipos estáticos pequeños y, crucialmente, omitiendo la información de longitud para tipos dinámicos como `string`, `bytes` o arrays dinámicos.
El problema central ocurre cuando `abi.encodePacked` se usa con dos o más argumentos de tipo dinámico adyacentes. Debido a que la longitud de cada argumento dinámico no se codifica, el límite entre ellos se vuelve ambiguo en la cadena de bytes resultante. Esta ambigüedad hace posible, a menudo de forma trivial, crear diferentes conjuntos de entradas lógicas que producen exactamente la misma secuencia de bytes empaquetada. Por ejemplo, `abi.encodePacked("a", "bc")` produce la misma salida de bytes que `abi.encodePacked("ab", "c")`.
Cuando esta salida de bytes idéntica se hashea posteriormente (p. ej., `keccak256(abi.encodePacked(...))`), resulta en el mismo valor de hash, una colisión de hash inducida por la codificación.
Esta vulnerabilidad de colisión de codificación puede explotarse de varias maneras si el hash resultante se utiliza en un contexto sensible a la seguridad:
Omisión de la verificación de firma: Un atacante puede tomar una firma válida creada para un conjunto de parámetros y reutilizarla con un conjunto de parámetros diferente y malicioso que produzca un hash que colisione. La verificación de la firma del contrato (`ecrecover`) tendrá éxito, otorgando una ejecución no autorizada.
Corrupción del estado mediante colisiones de claves de mapping: Si el hash propenso a colisiones se usa como clave en un mapping (`mapping(bytes32 => ...)`), un atacante puede diseñar entradas para generar una clave que colisione con la clave de un usuario legítimo, sobrescribiendo potencialmente sus datos, eludiendo controles de acceso o causando una denegación de servicio.
Problemas de autenticación de mensajes: La vulnerabilidad socava las comprobaciones que dependen del hash para garantizar la integridad de los datos, ya que diferentes mensajes lógicos pueden parecer idénticos después del hash.
Las consecuencias de una explotación exitosa pueden ser graves, incluyendo el acceso no autorizado a funciones o datos, el robo directo de fondos (Fund Theft), la corrupción crítica del estado y la denegación de servicio (DoS).
Las vulnerabilidades de pérdida de precisión se derivan de la dependencia de la aritmética de enteros y la falta de soporte nativo para números de punto flotante. Este diseño prioriza la ejecución determinista pero requiere que los desarrolladores administren manualmente los valores fraccionarios, creando oportunidades para errores.
El problema central es el truncamiento de la división entera: Solidity descarta los restos y redondea los resultados de la división hacia cero. Este comportamiento predecible puede ser explotado, a menudo a través de patrones como:
División antes de la multiplicación: Calcular (a / b) * c en lugar de (a * c) / b trunca el resultado intermedio a / b, amplificando la pérdida de precisión.
Redondeo hacia abajo a cero: Si el numerador A es menor que el denominador B (y ambos son positivos), A / B siempre resulta en 0. Esto es arriesgado para cálculos que involucran pequeñas comisiones, recompensas o conversiones de tokens.
Los atacantes explotan estas propiedades matemáticas para manipular la lógica del contrato con fines de lucro financiero. Las estrategias comunes incluyen:
Manipulación de estado/precio: Provocar errores de redondeo para distorsionar valores críticos del protocolo como tasas de cambio, reservas de pool, precios de participaciones de bóveda o ratios de colateral, que luego pueden ser explotados en transacciones posteriores.
Ataque a casos límite: Usar transacciones con entradas muy pequeñas o entradas diseñadas para interactuar con grandes valores internos para maximizar el impacto del truncamiento, lo que a menudo hace que los cálculos resulten en cero.
Los ataques exitosos de pérdida de precisión pueden llevar a consecuencias negativas significativas:
Costos reducidos: Los atacantes pagan comisiones/costos más bajos o nulos.
Ganancias infladas: Los atacantes reciben ilegítimamente más tokens, participaciones o recompensas.
Oportunidades de arbitraje: Crear diferencias de precios artificiales dentro del protocolo para que los atacantes las exploten.
Elusión de mecanismos de riesgo: Omitir liquidaciones u otras comprobaciones de seguridad debido a cálculos inexactos.
Agotamiento gradual de fondos: Extraer valor a través de transacciones repetidas que explotan pequeños errores de redondeo ("ataques de 1 wei").
La mitigación implica un manejo aritmético cuidadoso, como realizar multiplicaciones antes que divisiones, usar escalado numérico (simulando matemáticas de punto fijo), emplear bibliotecas matemáticas especializadas e implementar una lógica de redondeo apropiada. Las comprobaciones estándar de desbordamiento (como SafeMath o Solidity >=0.8) no evitan la pérdida de precisión por división.
Esta vulnerabilidad ocurre cuando un contrato inteligente utiliza incorrectamente msg.value dentro de un bucle. El problema principal radica en que msg.value permanece constante durante todo el contexto de ejecución de una transacción. Si un bucle itera múltiples veces, realizando comprobaciones o acciones basadas en este msg.value inicial en cada iteración sin rastrear correctamente el valor acumulado procesado o gastado a lo largo de esas iteraciones, se crea una oportunidad de explotación (exploit).
Un atacante puede explotar esto enviando una cantidad específica de Ether para activar la función vulnerable. Dentro del bucle, comprobaciones como require(msg.value >= amount_per_item) podrían pasar repetidamente, o las actualizaciones de estado podrían usar incorrectamente el valor inicial completo de msg.value varias veces. Esto sucede porque la lógica del contrato no tiene en cuenta el valor efectivamente 'gastado' o asignado en iteraciones anteriores del mismo bucle.
Este fallo permite a un atacante desencadenar acciones (como transferencias de Ether o créditos de saldo interno) cuyo valor total excede significativamente el Ether que realmente envió con la transacción.
ecrecover es un precompilado esencial de la EVM que permite a los contratos inteligentes recuperar la dirección del firmante a partir del hash de un mensaje y una firma ECDSA (v, r, s). Esto habilita funcionalidades cruciales como la verificación de mensajes firmados fuera de la cadena (off-chain) para meta-transacciones o funciones `permit`. Sin embargo, su uso directo presenta riesgos de seguridad significativos, a menudo subestimados, si no se maneja con cuidado.
Vulnerabilidad de retorno de dirección cero: Un problema crítico surge del manejo de errores particular de `ecrecover`. Cuando se le presenta una firma inválida o matemáticamente imposible, no revierte la transacción. En su lugar, falla silenciosamente y devuelve la dirección cero (`address(0)`). Los contratos que llaman a `ecrecover` pero carecen de una verificación de dirección cero son muy vulnerables. Un atacante puede enviar intencionalmente datos de firma inválidos, haciendo que `ecrecover` devuelva `address(0)`. Si esta salida no se verifica y rechaza explícitamente, el contrato podría proceder incorrectamente, tratando `address(0)` como el firmante legítimo. Esto puede llevar a consecuencias graves, como cambios de estado no autorizados, emisiones de eventos incorrectas o concesión de permisos, especialmente si la dirección cero tiene privilegios especiales o un estado significativo dentro de la lógica específica del contrato. El código robusto siempre debe validar `recoveredAddress != address(0)` inmediatamente después de la llamada a `ecrecover`.
Vulnerabilidad de maleabilidad de la firma: El segundo riesgo principal surge de una propiedad inherente del propio algoritmo ECDSA: la maleabilidad de la firma. Para cualquier mensaje y clave privada dados, pueden existir múltiples representaciones de firma distintas pero criptográficamente válidas (específicamente, una firma que usa el componente `s` a menudo se puede transformar en una firma válida usando `n-s`, donde `n` es el orden de la curva). Esto se convierte en una vulnerabilidad si los contratos asumen incorrectamente que una firma para un mensaje es única. Los atacantes pueden explotar esto eludiendo las comprobaciones de unicidad. Por ejemplo, si un contrato utiliza el hash de la propia firma como nonce para prevenir repeticiones (un patrón defectuoso), un atacante puede tomar una firma válida, calcular su contraparte maleable y enviarla para ejecutar la acción nuevamente, ya que los hashes de las firmas serán diferentes. También puede causar un comportamiento inesperado o repeticiones en sistemas que esperan una forma de firma específica, si los sistemas externos o partes de la lógica del contrato no fueron diseñados para manejar ambas formas de firma válidas. La mitigación efectiva implica forzar la canonización de la firma, una verificación implementada de forma robusta en bibliotecas estándar como ECDSA de OpenZeppelin, que debe preferirse sobre el uso directo de `ecrecover`.
Ataque de Repetición Intercadenas (CCRA): Una transacción ejecutada válidamente en una cadena EVM es capturada y reenviada con éxito en una cadena EVM diferente. Esto explota similitudes en los formatos de transacción y las firmas entre cadenas, especialmente cuando las transacciones carecen de identificadores de cadena únicos (Chain ID). La bifurcación dura (hard fork) de Ethereum/Ethereum Classic es un ejemplo clásico donde este riesgo se materializó. EIP-155 se introdujo para mitigar esto incrustando el Chain ID en las firmas de transacción estándar, haciéndolas específicas de cada cadena. Sin embargo, los contratos inteligentes (smart contracts) que utilizan verificación de firma personalizada también deben comprobar explícitamente el Chain ID. El exploit de 20 millones de dólares en Optimism contra Wintermute fue resultado de la falta de dicha comprobación del Chain ID en un contrato desplegado entre cadenas (cross-chain).
Repetición a Nivel de Contrato Inteligente (Misma Cadena): Un mensaje firmado o una transacción se repite contra el mismo contrato inteligente, o potencialmente contra otro contrato en la misma cadena. Esto típicamente explota vulnerabilidades dentro de la lógica propia del contrato, particularmente en esquemas de verificación de firma personalizados utilizados para características como metatransacciones o funciones `permit` de ERC-20. El fallo más común es la ausencia o implementación incorrecta de un nonce a nivel de aplicación. Un nonce («número usado una vez») es un contador único asociado al firmante que debe incluirse en el resumen del mensaje firmado y ser rastreado on-chain por el contrato para asegurar que cada firma específica autorice solo una acción.
El soporte de Solidity para la herencia múltiple permite que un contrato herede características de varios contratos padre simultáneamente. Aunque es potente para la reutilización de código, esto introduce una posible ambigüedad, conocida como el "Problema del Diamante": si dos o más contratos base definen una función con el mismo nombre y parámetros.
Para resolver esto, Solidity emplea el algoritmo de linealización C3 para establecer un Orden de Resolución de Métodos (Method Resolution Order, MRO) único y determinista para cada contrato. Este MRO dicta la secuencia precisa en la que se verifican los contratos base al resolver llamadas a funciones.
La vulnerabilidad surge directamente de cómo se determina este MRO. El factor crucial es el orden en que el desarrollador lista los contratos base en la declaración `is`. Solidity requiere listar los contratos desde el "más base" hasta el "más derivado". Este orden especificado influye directamente en el MRO final generado por el algoritmo C3.
La vulnerabilidad ocurre cuando el desarrollador proporciona un orden de herencia que no coincide con la jerarquía lógica o prioridad prevista. Si un contrato más general se lista después de uno más específico, o si el orden causa que el MRO priorice una implementación de función no intencionada, el contrato puede comportarse de manera inesperada. Por ejemplo, una llamada podría resolverse a una función base que carece de controles de seguridad críticos o lógica actualizada que se implementó en una anulación de contrato derivado prevista pero ordenada incorrectamente.
Las consecuencias de ejecutar la función incorrecta debido a un orden de herencia incorrecto incluyen la elusión de los controles de acceso, la ejecución de lógica de negocio obsoleta o incorrecta, la corrupción del estado y posibles pérdidas financieras. Esencialmente, el flujo de ejecución real del contrato se desvía del diseño del desarrollador, socavando la seguridad y la funcionalidad.
El Valor Máximo Extraíble (Maximal Extractable Value - MEV) representa el beneficio que los productores de bloques y los buscadores especializados (searchers) pueden capturar manipulando la inclusión y el orden de las transacciones dentro de un bloque, más allá de las recompensas de bloque estándar y las comisiones de gas. Originalmente denominado "Valor Extraíble por Mineros" (Miner Extractable Value), el nombre evolucionó a "Máximo" para reflejar su avidez por la recompensa.
El MEV surge porque las transacciones pendientes a menudo se encuentran en un área de espera pública llamada mempool, visible para cualquiera. Los productores de bloques tienen la autoridad para decidir el orden final de las transacciones en un bloque. Bots automatizados, gestionados por "buscadores" (searchers), monitorean constantemente el mempool, simulan posibles resultados y explotan oportunidades rentables ordenando estratégicamente las transacciones, a menudo utilizando subastas de gas (Priority Gas Auctions - PGA) para asegurar una colocación preferencial.
Las estrategias comunes de MEV, a menudo vistas como ataques, incluyen:
Front-Running: Colocar la transacción de un atacante antes de la transacción de una víctima (por ejemplo, una gran operación en un DEX) para beneficiarse del impacto anticipado en el precio.
Ataques Sándwich (Sandwich Attacks): Una combinación de front-running y back-running (seguir por detrás) la operación DEX de una víctima para capturar la diferencia de precio (deslizamiento o slippage) causada por la manipulación.
Liquidez Justo a Tiempo (JIT Liquidity): Añadir y eliminar temporalmente liquidez alrededor de un gran intercambio (swap) en DEX de liquidez concentrada para capturar comisiones.
Manipulación de Oráculos (Oracle Manipulation): Explotar actualizaciones o inexactitudes de los oráculos de precios para obtener beneficios, lo que a menudo afecta a los protocolos de préstamos.
Otros tipos incluyen el sniping de NFT (NFT sniping) y los ataques de polvo (dust attacks).
Las consecuencias para los usuarios incluyen mayores costos de transacción debido a las guerras de gas (gas wars), peores precios de ejecución (deslizamiento o slippage) en las operaciones, pérdida impermanente (impermanent loss) exacerbada para los proveedores de liquidez y liquidaciones activadas injustamente.
Las estrategias de mitigación tienen como objetivo reducir el impacto negativo del MEV. Enviar transacciones a través de mempools privados o relés (como Flashbots Protect o MEV Blocker) las oculta de la vista pública. Los diseños a nivel de aplicación como los esquemas Commit-Reveal (compromiso-revelación) ocultan los detalles de la transacción hasta que se fija el orden, mientras que el uso de precios promedio ponderados por tiempo (Time-Weighted Average Prices - TWAP) para los oráculos puede reducir los riesgos de manipulación.