Análisis de smart contracts
27.03.2024

Trampas ocultas en Honeypot. ¿Cómo reconocer el cebo y evitar perder activos?

Los contratos inteligentes Honeypot son un tipo de contratos inteligentes fraudulentos que parecen atractivos para los usuarios y prometen beneficios financieros. Sin embargo, estos contratos contienen trampas ocultas o código malicioso, que hacen que los usuarios pierdan sus fondos por el contrario.

Para protegerse de los contratos inteligentes Honeypot es necesario investigar a fondo y verificar el código del contrato antes de comprometerse con él. El mundo de los contratos inteligentes y blockchain está en constante evolución, con nuevas vulnerabilidades y métodos de engaño que aparecen regularmente, por lo que es vital para los desarrolladores, auditores y usuarios mantenerse al día con las últimas tendencias y desarrollos en esta área.

Hay muchos ejemplos de contratos inteligentes Honeypot. En este artículo veremos algunos de ellos.

1. Honeypot con función Fallback

En este tipo de honeypot, el contrato tiene una función vulnerable de "reversión" que aparentemente permite a cualquiera convertirse en propietario del contrato y eliminar su saldo. Sin embargo, dicha característica contiene una condición oculta que impide su ejecución con éxito.

Código de ejemplo:


pragma solidity 0.4.18

contract FallbackHoneypot {
    address public owner;

    function() public payable {
        if (owner == 0) {
            owner = msg.sender;
        } else {
            revert();
        }
    }

    function withdraw() public {
        require(msg.sender == owner);
        msg.sender.transfer(this.balance);
    }
}

En este ejemplo, la función fallback permite que cualquier usuario se convierta en el propietario. Sin embargo, el operador revert() asegura que el cambio de propietario nunca ocurra, dejando los fondos del atacante atrapados en un contrato.

2. Honeypot de condiciones ocultas

Estos contratos contienen una condición o requisito oculto que debe cumplirse para que la función aparentemente vulnerable tenga éxito. Los usuarios pueden pensar que pueden utilizar el contrato, pero la condición oculta garantiza que no pueden hacerlo.

Código de ejemplo:


pragma solidity 0.4.18

contract HiddenConditionHoneypot {
    uint256 public counter = 0;

    function deposit() public payable {
        require(msg.value == 1 ether);
        counter++;
    }

    function withdraw() public {
        require(counter > 100);
        msg.sender.transfer(this.balance);
    }
}

En este contrato, el usuario puede depositar 1 éter, y parecería que después de 100 depósitos, el usuario puede retirar todo el saldo del contrato. Sin embargo, la condición contador > 100 asegura que la retirada sólo es posible después de 101 depósitos, lo que retrasa los fondos en el contrato.

3. Honeypot de reentrada

En este tipo de honeypot, el contrato es vulnerable a un ataque de reentrada, en el que un atacante puede invocar repetidamente una función del contrato durante la retirada. Sin embargo, el contrato contiene un mecanismo oculto que impide su explotación con éxito.

Código de ejemplo:


pragma solidity 0.4.18

contract ReentrancyHoneypot {
    mapping(address => uint256) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint256 _amount) public {
        require(balances[msg.sender] >= _amount);
        if (msg.sender.call.value(_amount)()) {
            balances[msg.sender] -= _amount;
        }
    }
}

En este ejemplo, el contrato es vulnerable a un ataque de reentrada debido al uso de msg.sender.call.value(_amount)(). Sin embargo, el contrato no permite a los usuarios recargar una cuenta con una cantidad superior al saldo inicial, lo que retrasa sus fondos.

honeypot de reentrada

4. Honeypot de límite de gas

En este tipo de honeypot, el contrato parece permitir al usuario retirar fondos, pero la cantidad de gas necesaria para realizar la función de retirada supera el límite de bloqueo de gas, lo que imposibilita la transacción.

Código de ejemplo:


pragma solidity 0.4.18

contract GasLimitHoneypot {
    uint256 constant public numArrayElements = 1000;
    uint256[numArrayElements] public someArray;

    function deposit() public payable {
        for (uint256 i = 0; i < numArrayElements; i++) {
            someArray[i] = 0;
        }
    }

    function withdraw() public {
        uint256 balance = address(this).balance;
        for (uint256 i = 0; i < numArrayElements; i++) {
            someArray[i] = 1;
        }
        msg.sender.transfer(balance);
    }
}

En este ejemplo, la función de depósito inicializa una matriz con 1000 elementos. La función retirar transfiere el saldo del contrato al remitente, pero modifica los elementos del array. El gas necesario para ejecutar el bucle supera el límite del bloque de gas, lo que impide la retirada y la retrasa en el contrato.

5. Honeypot de manipulación de marcas de tiempo

En este tipo de honeypot, el contrato utiliza la marca de tiempo del blockchain de Ethereum como condición de ejecución. Sin embargo, como los mineros pueden manipular la marca de tiempo del bloque, el contrato se vuelve vulnerable.

Código de ejemplo:


pragma solidity 0.4.18

contract TimestampHoneypot {
    uint256 public lastInteraction;
    uint256 public prize;

    function deposit() public payable {
        lastInteraction = now;
        prize += msg.value;
    }

    function withdraw() public {
        require(now >= lastInteraction + 1 hours);
        msg.sender.transfer(prize);
        prize = 0;
    }
}

En este contrato, los usuarios pueden depositar fondos y retirar todo el saldo del contrato transcurrida una hora desde la última interacción. Sin embargo, dado que los mineros pueden manipular la marca de tiempo del bloque, pueden activar la función de retirada antes de lo previsto y, por tanto, robar fondos.

6. Honeypot de transferencia oculta

En este tipo de honeypot, el contrato contiene una transferencia oculta de fondos a la dirección del atacante, que da la apariencia de un contrato legítimo pero que en realidad resulta en una pérdida de fondos.

Código de ejemplo:


pragma solidity 0.4.18

contract HiddenTransferHoneypot {
    address public owner;

    constructor() public {
        owner = msg.sender;
    }

    function deposit() public payable {
        if (msg.value >= 1 ether) {
            owner.transfer(msg.value);
        }
    }

    function withdraw() public {
        require(msg.sender == owner);
        owner.transfer(address(this).balance);
    }
}

En este ejemplo, el contrato parece permitir depósitos y retiradas. Sin embargo, la función de recarga contiene una transferencia oculta de fondos al propietario (el atacante) si el importe de la recarga es igual o superior a 1 éter. Los usuarios desprevenidos que recarguen sus cuentas perderán sus éteres, ya que serán transferidos a la dirección del atacante.

7. Honeypot de reentrada

El honeypot de reentrada se basa en explotar una vulnerabilidad de reentrada en un contrato, donde una función del contrato puede ser llamada recursivamente antes de que se actualice su estado.

Código de ejemplo:


pragma solidity 0.4.18

contract ReentrancyHoneypot {
    mapping(address => uint256) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw() public {
        uint256 amount = balances[msg.sender];
        (bool success,) = msg.sender.call.value(amount)("");
        require(success, "Withdrawal failed.");
        balances[msg.sender] = 0;
    }
}

En este ejemplo, el contrato permite realizar depósitos y retiradas. Sin embargo, es vulnerable a los ataques de reentrada. Un atacante podría crear un contrato malicioso que llamara recursivamente a la función withdraw antes de que el saldo se pusiera a cero, agotando todo el saldo del contrato.

8. Tipo de colada y rebosadero

En este tipo de honeypot, el atacante utiliza la conversión de tipos y los desbordamientos de enteros para engañar a los usuarios haciéndoles creer que el contrato es seguro, pero en realidad contiene trampas ocultas.

Código de ejemplo:


pragma solidity 0.4.18

contract TypeCastingHoneypot {
    uint8 public count = 0;
    uint256 public reward = 1 ether;

    function increment() public payable {
        require(msg.value >= reward);

        uint8 prevCount = count;
        count++;

        if (count < prevCount) {
            msg.sender.transfer(address(this).balance);
        }
    }
}

En este ejemplo, el contrato recompensa a los usuarios que incrementan la variable count cuando envían una cantidad igual o superior a la cantidad de la recompensa. Sin embargo, debido al uso de uint8 para la variable count, se produce un desbordamiento de enteros cuando se alcanza el valor 255. Tras el desbordamiento, la cuenta se reinicia a 0 y un atacante puede obtener todo el saldo del contrato.

tipo de colada y rebosadero

9. Honeypot de llamada delegada

En este tipo de honeypot, el atacante utiliza la función delegatecall para ejecutar código malicioso en nombre de un contrato aparentemente seguro.

Código de ejemplo:


pragma solidity 0.4.18

contract DelegateCallHoneypot {
    address public owner;

    constructor() public {
        owner = msg.sender;
    }

    function() external payable {}

    function withdraw() public {
        require(msg.sender == owner);
        owner.transfer(address(this).balance);
    }

    function execute(address _target, bytes memory _data) public {
        require(msg.sender == owner);
        (bool success,) = _target.delegatecall(_data);
        require(success, "Execution failed.");
    }
}

En este ejemplo, el contrato permite depósitos, retiros y ejecución de código a través de la función execute. Sin embargo, un atacante podría crear código malicioso que pudiera manipular el estado del contrato, incluida la variable owner, ejecutando la función delegatecall. Cambiando el propietario, un atacante podría obtener el control del contrato y vaciar su saldo.

10. Honeypot de manipulación de almacenamiento oculto

En este tipo de honeypot, el atacante manipula de forma encubierta las variables de almacenamiento para engañar a los usuarios y que tomen el control del contrato.

Código de ejemplo:


pragma solidity 0.4.18

contract HiddenStorageManipulation {
    mapping(address => uint256) public balances;
    uint256 public totalSupply;
    address public owner;

    constructor() public {
        owner = msg.sender;
        balances[owner] = 1000;
        totalSupply = 1000;
    }

    function transfer(address _to, uint256 _value) public {
        require(balances[msg.sender] >= _value);
        balances[msg.sender] -= _value;
        balances[_to] += _value;

        if (totalSupply - balances[owner] >= 1000) {
            owner = _to;
        }
    }
}

En este ejemplo, el contrato parece un simple contrato de tokens. Los usuarios pueden pasarse tokens unos a otros, y el propietario se establece inicialmente como el creador del contrato. Sin embargo, un atacante puede manipular de forma encubierta las variables totalSupply y balances para recuperar el control del contrato. Cuando la diferencia entre totalSupply y balances[owner] alcanza 1000, la variable owner se actualiza, permitiendo al atacante recuperar el control del contrato.

11. Nombre de la función honeypot de colisión

En este tipo de honeypot, el atacante utiliza colisiones de nombres de funciones para engañar a los usuarios y hacer que llamen a la función equivocada.

Código de ejemplo:


pragma solidity 0.4.18

contract FunctionNameCollision {
    address public owner;

    constructor() public {
        owner = msg.sender;
    }

    function withdraw() external {
        require(msg.sender == owner);
        msg.sender.transfer(address(this).balance);
    }

    function withdraw(uint256 amount) external {
        require(msg.sender == owner);
        owner.transfer(amount);
    }

    function() external payable {}
}

En este ejemplo, el contrato tiene dos funciones de retirada. Una de ellas permite al propietario retirar todo el saldo y la otra permite al propietario retirar una cantidad determinada. Sin embargo, si el usuario intenta retirar una cantidad determinada llamando a withdraw(uint256), debido a una colisión de nombres de funciones, el usuario llamará inadvertidamente a withdraw() sin argumentos, lo que resultará en la retirada de todo el saldo del contrato.

12. Honeypot de vulnerabilidad delegatecall

En este tipo de honeypot, el atacante utiliza delegatecall para ejecutar una función maliciosa en otro contrato, lo que puede conducir al robo de fondos de los usuarios.

Código de ejemplo:


pragma solidity 0.4.18

contract DelegatecallVulnerability {
    address public owner;

    constructor() public {
        owner = msg.sender;
    }

    function withdraw() external {
        require(msg.sender == owner);
        msg.sender.transfer(address(this).balance);
    }

    function () external payable {
        if (msg.value > 1 ether) {
            this.delegatecall(msg.data);
        }
    }
}

contract Malicious {
    function withdraw() external {
        msg.sender.transfer(address(this).balance);
    }
}

En este ejemplo, el contrato parece inocuo, con una simple función withdraw() que permite al propietario retirar el saldo del contrato. Sin embargo, el contrato también contiene una función fallback que, al recibir más de 1 éter, realiza una llamada delegada de otro contrato utilizando msg.data. Si un atacante crea un contrato malicioso con withdraw() que roba el saldo del contrato y envía más de 1 éter al contrato víctima, la llamada delegada ejecutará la función maliciosa withdraw(), robando el saldo del contrato.

vulnerabilidad delegatecall

13. Pseudo-integers overflow honeypot

Este tipo de honeypot se basa en el hecho de que Solidity no tiene soporte integrado para números de coma flotante. Los atacantes pueden crear contratos que aparentemente utilicen números decimales, pero que en realidad manipulen números enteros de forma fraudulenta.

Código de ejemplo:


pragma solidity 0.4.18

contract PseudoIntegersOverflow {
    mapping(address => uint256) public balances;

    function deposit() external payable {
        require(msg.value > 0);
        uint256 pseudoValue = msg.value * 100;
        balances[msg.sender] += pseudoValue;
    }

    function withdraw(uint256 amount) external {
        uint256 pseudoAmount = amount * 100;
        require(balances[msg.sender] >= pseudoAmount);
        balances[msg.sender] -= pseudoAmount;
        msg.sender.transfer(amount);
    }
}

Este ejemplo da la impresión de que el contrato gestiona el saldo del usuario con precisión decimal multiplicando los valores por 100 antes de guardarlos. Sin embargo, este enfoque es engañoso porque Solidity no admite números en coma flotante. Cuando el usuario intenta retirar sus fondos, el contrato calcula incorrectamente la pseudo-suma debido al truncamiento de enteros, y es posible que el usuario no pueda retirar todo su saldo.

Para protegerse contra este tipo de honeypot, debe comprender las limitaciones del lenguaje Solidity y cómo maneja las operaciones numéricas. Tenga cuidado cuando trabaje con contratos que afirmen admitir precisión decimal y compruebe siempre el código del contrato para detectar posibles problemas.

14. Comisiones ocultas en los contratos inteligentes

Algunos contratos inteligentes maliciosos pueden imponer pagos ocultos a los usuarios sin su conocimiento. Estos pagos pueden estar camuflados en el código del contrato o activarse solo en determinadas condiciones.

Código de ejemplo:


pragma solidity 0.4.18

contract HiddenFees {
    address public owner;
    uint256 public feePercentage;

    constructor(uint256 _feePercentage) public {
        owner = msg.sender;
        feePercentage = _feePercentage;
    }

    function withdraw(uint256 amount) external {
        uint256 fee = amount * feePercentage / 100;
        uint256 netAmount = amount - fee;
        require(address(this).balance >= netAmount);
        msg.sender.transfer(netAmount);
        owner.transfer(fee);
    }
}

En este ejemplo, el creador del contrato establece una tarifa (feePercentage) al desplegar el contrato. Al interactuar con el contrato, los usuarios pueden no ser conscientes de esta comisión. Cuando un usuario retira sus fondos, el contrato calcula la comisión, la resta del importe retirado y envía el resto al usuario. La comisión se transfiere entonces al propietario del contrato.

Para evitar caer en esta trampa, revisa detenidamente el código del contrato y asegúrate de que entiendes todas las comisiones asociadas. Busca cálculos ocultos o encubiertos.

15. Manipulación de estados ocultos

En algunos casos, los contratos maliciosos pueden permitir al propietario del contrato o a un atacante manipular el estado interno del contrato, provocando consecuencias inesperadas para otros usuarios.

Código de ejemplo:


pragma solidity 0.4.18

contract HiddenStateManipulation {
    address public owner;
    mapping(address => uint256) public balances;

    constructor() public {
        owner = msg.sender;
    }

    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint256 amount) external {
        require(balances[msg.sender] >= amount);
        msg.sender.transfer(amount);
        balances[msg.sender] -= amount;
    }

    function manipulateBalance(address user, uint256 newBalance) external {
        require(msg.sender == owner);
        balances[user] = newBalance;
    }
}

En este ejemplo, el contrato permite a los usuarios depositar y retirar fondos. Sin embargo, la función manipulateBalance permite al propietario del contrato cambiar arbitrariamente el saldo de cualquier usuario. Esto puede provocar pérdidas inesperadas a los usuarios o permitir al propietario del contrato robar fondos.

El código del contrato debe examinarse en busca de características que permitan la manipulación del estado, especialmente si sólo el propietario del contrato o determinadas direcciones tienen acceso a ellas.

manipulación de estados ocultos

16. Robo de fichas ocultas

En algunos casos, los contratos maliciosos pueden contener características ocultas que permitan al propietario del contrato o al atacante robar tokens a usuarios desprevenidos.

Código de ejemplo:


pragma solidity 0.4.18

contract HiddenTokenStealing {
    address public owner;
    mapping(address => uint256) public balances;

    constructor() public {
        owner = msg.sender;
    }

    function deposit(IERC20 token, uint256 amount) external {
        require(token.transferFrom(msg.sender, address(this), amount));
        balances[msg.sender] += amount;
    }

    function withdraw(IERC20 token, uint256 amount) external {
        require(balances[msg.sender] >= amount);
        require(token.transfer(msg.sender, amount));
        balances[msg.sender] -= amount;
    }

    function stealTokens(IERC20 token, address user, uint256 amount) external {
        require(msg.sender == owner);
        require(token.transfer(owner, amount));
        balances[user] -= amount;
    }
}

En este ejemplo, el contrato permite a los usuarios depositar y retirar tokens ERC20. Sin embargo, la función stealTokens permite al propietario del contrato robar tokens a cualquier usuario.

Conclusión:

Honeypot es sólo uno de los muchos esquemas fraudulentos en DeFi que utiliza no sólo la restricción de retirada, sino también las diversas trampas ocultas descritas anteriormente.

Para evitar caer en el anzuelo es necesario comprender las peculiaridades del esquema fraudulento, las sutilezas del lenguaje de programación Solidity y estudiar a fondo los contratos inteligentes antes de interactuar con ellos.

Manteniéndose informado y llevando a cabo una diligencia debida exhaustiva, puede minimizar los riesgos asociados al uso de contratos inteligentes.

 

Si tiene alguna duda sobre la seguridad o funcionalidad de un contrato inteligente utilice nuestra plataforma Lotus Market.

 

La plataforma Lotus Market fue creada por un equipo de desarrolladores experimentados y auditores profesionales. El objetivo del proyecto es minimizar los riesgos de invertir en tokens fraudulentos y ayudar a comerciar con criptodivisas de forma segura y cómoda.

Saludos, equipo de Lotus Market.

All posts

Connect to a wallet

Metamask