Los contratos inteligentes son códigos que se ejecutan automáticamente y constituyen la columna vertebral del ecosistema Web3. Los contratos inteligentes actúan como los pilares fundamentales del ecosistema Web3, equilibrando con precisión miles de millones en una red abierta. Hoy hablaremos de las vulnerabilidades que los auditores deben buscar: «floating pragma», el phishing con «tx.origin» y la manipulación de la marca de tiempo del bloque. Este es un buen punto de partida si quieres aprender sobre Solidity y cómo auditar contratos inteligentes. Este es uno de los artículos de una serie dedicada a la auditoría de contratos inteligentes en Solidity. La serie tratará sobre las vulnerabilidades y los recursos que utilizan los auditores de contratos inteligentes.
Pragma flotante
Cuando se utiliza un pragma flotante en Solidity, el compilador puede utilizar cualquier versión de Solidity que sea igual o posterior a la versión especificada. Sin embargo, esta flexibilidad conlleva riesgos. Tus contratos inteligentes podrían compilarse utilizando una versión del compilador obsoleta o incompatible, lo que puede dar lugar a errores o vulnerabilidades.
Para que quede más claro, la instrucción `pragma solidity ^0.7.0;` es un pragma flotante. El símbolo del asterisco (`^`) desempeña aquí un papel fundamental. Permite utilizar cualquier versión del compilador de Solidity que sea compatible con `0.7.0` pero inferior a `0.8.0`. Esto significa que se pueden utilizar futuras versiones menores (por ejemplo, `0.7.1`, `0.7.2`, etc.), al tiempo que se evitan de forma efectiva los cambios que rompen la compatibilidad en las versiones mayores (por ejemplo, `0.9.0`), lo que aporta una sensación de estabilidad a tu código.
Sin embargo, hay un caso en el que sí está bien utilizar un pragma flotante, y es cuando se trabaja con bibliotecas o paquetes. En otros casos, si no se especifica una versión concreta, los demás desarrolladores tendrían que ajustar manualmente el código para que funcionara correctamente en sus ordenadores.
Phishing con Tx.Origin
En Solidity, existe una variable global denominada «tx.origin». Esta variable proporciona la dirección o la cuenta del iniciador que ha activado la transacción. Sin embargo, es fundamental actuar con precaución al utilizar «tx.origin» como criterio para conceder autorización.
Imaginemos el siguiente escenario: una persona manipula un contrato inteligente que se basa en «tx.origin» para determinar erróneamente su derecho a realizar una acción, incluso si dicho derecho no es válido. Esto se debe a que «tx.origin» revela quién inició el proceso de la transacción, y no la entidad actual que intenta realizar la acción.
Por consiguiente, si se depende en gran medida de «tx.origin» para la autorización, se podría conceder acceso involuntario a una entidad no autorizada. Esta analogía se asemeja a permitir la entrada a alguien basándose únicamente en su proximidad a otra persona que posee una llave, lo cual carece de seguridad por naturaleza.
Contrato inteligente de la víctima
En los contratos mencionados anteriormente, «Wallet» está diseñado para gestionar el almacenamiento y las retiradas de fondos, mientras que «Attack» es una creación de un actor malintencionado cuyo objetivo es explotar el contrato inicial. Cabe destacar que el mecanismo de autorización de la función de transferencia se basa en el uso de «tx.origin».
En los contratos mencionados anteriormente, «Wallet» está diseñado para gestionar el almacenamiento y las retiradas de fondos, mientras que «Attack» es una creación de un actor malintencionado cuyo objetivo es explotar el contrato inicial. Cabe destacar que el mecanismo de autorización de la función de transferencia se basa en el uso de «tx.origin».
Contrato inteligente del atacante
Ahora bien, aquí está la complejidad: cuando el titular del contrato «Wallet» envía una transacción con gas suficiente a la dirección del contrato «Attack», se activa la invocación de la función de reserva. Esto, a su vez, provoca la ejecución de la función de transferencia desde el contrato «Wallet», utilizando «attacker» como parámetro. En consecuencia, todos los recursos financieros alojados en el contrato «Wallet» serán extraídos y transferidos a la dirección designada por el atacante. Esto ocurre porque la identidad que ha iniciado el proceso de llamada a la función es la del objetivo, que es el propietario del contrato «Wallet».
Por lo tanto, el valor de «tx.origin» coincidirá con la identidad del propietario, lo que cumplirá la condición «require» y permitirá que la operación continúe según lo previsto.
Recomendación
Se recomienda utilizar «msg.sender» en lugar de «tx.origin».
Manipulación de la marca de tiempo de los bloques
Cada bloque de un sistema de cadena de bloques tiene una cabecera que incluye un campo de marca de tiempo, un dato fundamental que permite a la red establecer un orden cronológico y hacer un seguimiento de la secuencia de eventos. Curiosamente, este campo de marca de tiempo no es un elemento inmutable, sino que lo establece el minero responsable de generar el bloque.
El minero puede manipular el campo de marca de tiempo hasta cierto punto, aunque con ciertas restricciones. Esta capacidad le permite influir en el sistema dentro de unos límites temporales concretos.
Para establecer la marca de tiempo de un bloque, el minero debe ganar la competición por minar el siguiente bloque de la cadena de bloques. Como validador responsable de las transacciones, el minero debe cumplir dos condiciones fundamentales:
- La siguiente marca de tiempo debe seguir cronológicamente a la marca de tiempo del bloque anterior. La cadena de bloques mantiene su integridad y continuidad al progresar lógicamente a lo largo del tiempo.
- La marca de tiempo no se puede fijar demasiado lejos en el futuro. Esta limitación garantiza que la red permanezca sincronizada, evitando posibles interrupciones derivadas de bloques fijados demasiado por delante del tiempo real.
Cuando un minero consigue minar con éxito un nuevo bloque y se convierte en el productor del mismo, puede ajustar ligeramente la marca de tiempo del bloque. Esto supone una ventaja única para el minero, que puede aprovechar este control para influir sutilmente en la secuencia de eventos de la cadena de bloques.
Contrato de riesgo
El contrato de ruleta proporcionado es un juego en el que los jugadores pueden ganar todo el saldo de Ether del contrato si envían una transacción en un momento concreto. Los jugadores pueden participar llamando a la función «spin» y enviando 5 Ethers al contrato. La victoria depende de si la marca de tiempo del bloque es divisible por 12 y, si se cumple esta condición, el jugador recibe la totalidad del Ether almacenado en el contrato.
Los mineros malintencionados pueden atacar este contrato llamando a su función «spin» y enviando 5 Ethers. A continuación, manipulan la marca de tiempo del próximo bloque para asegurarse de que sea divisible por 12. Si consiguen minar el siguiente bloque con la marca de tiempo manipulada, el atacante se quedará con todo el saldo de Ether almacenado en el contrato inteligente como recompensa.
Recomendación
No utilices ` block.timestamp ` como fuente de entropía y de números aleatorios. Es más seguro integrar una fuente de entropía más segura e imparcial, como la VRF (función aleatoria verificable) de Chainlink.
En conclusión
A la hora de desarrollar contratos inteligentes en Solidity, es fundamental seguir las mejores prácticas para mejorar la seguridad y la fiabilidad. Evita el uso de «pragma» flotante para reducir el riesgo de errores o vulnerabilidades causados por versiones del compilador obsoletas o incompatibles. En lugar de basarte en `tx.origin` para verificar el origen de la transacción, utiliza `msg.sender` para una mayor seguridad. En cuanto a la aleatoriedad criptográfica, evita utilizar `block.timestamp`, ya que los mineros pueden manipularlo; opta por Chainlink VRF (función aleatoria verificable) para generar aleatoriedad de forma segura. Las auditorías de contratos inteligentes, los programas de recompensa por errores y las revisiones son cruciales en todas las fases del desarrollo. Aumentan el número de personas que buscan vulnerabilidades y reducen la probabilidad de que se pasen por alto vulnerabilidades críticas.
Cuídate.
Únete a AuditOne como auditor: https://www.auditone.io/auditors
Reserva tu consulta gratuita sobre seguridad:
Google Calendar: https://calendar.app.google/Ai15eyQhiV5c1pBXA
Telegram: https://t.me/m_ndr
Artículos relacionados:
Auditoría de un contrato de Solidity: Episodio 1 — Ataque de reentrada
Auditoría de un contrato de Solidity: Episodio 2 - Delegatecall
Auditoría de un contrato de Solidity: Episodio 4 — Pruebas
Auditoría de un contrato de Solidity: Episodio 5 - Herramientas de pruebas automatizadas
Auditoría de un contrato de Solidity: Episodio 6 - Frontrunning
Auditoría de un contrato de Solidity: Episodio 7 — Documentación y elaboración de informes
Auditoría de un contrato de Solidity: Episodio 8 - Ventajas de la auditoría


.png)






