WEB3DEV

Cover image for Ataque Tx.origin no contrato inteligente
Adriano P. Araujo
Adriano P. Araujo

Posted on

Ataque Tx.origin no contrato inteligente

Aprenda sobre as vulnerabilidades populares de contratos inteligentes.

Foto de Mika Baumeister em Unsplash

Segurança inteligente do contrato:

Um contrato inteligente é um software, que é executado automaticamente quando critérios específicos são atendidos. Como contratos inteligentes são executados em uma rede blockchain descentralizada, eles são considerados seguros por padrão. No entanto, contratos inteligentes ainda podem ser vulneráveis a certos riscos de segurança, como erros de codificação, ataques maliciosos e testes insuficientes. Para reduzir o risco de problemas de segurança, é importante seguir as melhores práticas para escrever e implantar contratos inteligentes, como testar minuciosamente o código, usando técnicas de codificação seguras, e mantendo o código do contrato simples e modular. Também é importante auditar regularmente contratos inteligentes para identificar e solucionar possíveis vulnerabilidades.

O DAO Hack

Um dos maiores ataques que envolveram um contrato inteligente ocorreu em 2016, quando uma vulnerabilidade no código de contrato inteligente da Organização Autônoma Descentralizada ( DAO — Decentralized Autonomous Organization, no original ), foi explorada, resultando no roubo de cerca de $50 milhões em valor da criptomoeda Ether. O hack foi o resultado de um erro de codificação no contrato inteligente do DAO, que permitiu ao invasor retirar fundos repetidamente do contrato sem permissão. O incidente destacou a necessidade de testes e auditorias completas do código de contrato inteligente para garantir que ele seja seguro e livre de vulnerabilidades.

Vulnerabilidades populares no contrato inteligente:

Ataque de reentrada

Os ataques de reentrada ocorrem quando um contrato chama outro contrato e o segundo contrato volta ao primeiro contrato antes que o primeiro contrato conclua sua execução. Isso pode levar a comportamentos inesperados e potencial perda de fundos.

Para evitar isso, o Solidity oferece as funções revert() e require(), que interrompem a execução do contrato e revertem quaisquer alterações feitas se a condição especificada não for atendida.

Aqui está um exemplo de contrato vulnerável a um ataque de reentrada:


contract ReentrancyAttack {

    address public owner;

    uint public balance;

    constructor() public {

        owner = msg.sender;

        balance = 100;

    }

    function withdraw() public {

        // vulnerável ao ataque de reentrada

        if (balance >= 10) {

            balance -= 10;

            msg.sender.transfer(10);

        }

    }

}




Enter fullscreen mode Exit fullscreen mode

Para evitar essa vulnerabilidade, podemos usar a função require() para verificar se o saldo é suficiente antes de realizar a transferência:


contract ReentrancyAttack {

    address public owner;

    uint public balance;

    constructor() public {

        owner = msg.sender;

        balance = 100;

    }

    function withdraw() public {

        // preveni o ataque de reentrada

        require(balance >= 10, "Insufficient balance");

        balance -= 10;

        msg.sender.transfer(10);

    }

}



Enter fullscreen mode Exit fullscreen mode

Neste contrato atualizado, se o saldo for insuficiente para a transferência, a função require()  irá interromper a execução do contrato e reverterá as alterações feitas. Isso garantirá que o contrato não possa ser explorado por um ataque de reentrada.

Tx.origin

O ataque tx.origin em Solidity envolve a exploração do fato de que a variável global tx.origin nos contratos de Solidity é o endereço do chamador externo do contrato, e não o proprietário do contrato real. Isso permite que um invasor personifique o proprietário do contrato chamando o contrato de outro endereço, ignorando quaisquer controles de acesso ou verificações de permissão baseadas no endereço do proprietário do contrato.

Aqui está um exemplo de código vulnerável ao ataque tx.origin:


pragma solidity ^0.8.01;

contract MyContract { address public owner;

constructor() {

    owner = msg.sender;

}



function myFunction() public {

    require(msg.sender == owner, "Unauthorized access");

    // ..........

}

}



Enter fullscreen mode Exit fullscreen mode

Neste exemplo, o contrato verifica se o chamador da função myFunction ( ) é o proprietário do contrato. No entanto, como tx.origin é usado em vez do msg.sender, um invasor pode chamar myFunction ( ) de qualquer endereço e personificar o proprietário do contrato. Isso pode permitir que o invasor obtenha acesso não autorizado às funções e dados do contrato.

Sanduíche

O ataque sanduíche é uma vulnerabilidade que pode ocorrer nos contratos inteligentes de Solidity quando eles usam várias funções para modificar as mesmas variáveis de estado. Isso pode levar a um comportamento inesperado e potencialmente malicioso no contrato.

Aqui está um exemplo de um contrato de solidity vulnerável ao ataque sanduíche:


pragma solidity ^0.8.01;



contract SandwichAttack {

uint public balance;



function deposit(uint amount) public {

    balance += amount;

}



function withdraw(uint amount) public {

    balance -= amount;

}



function transfer(address to, uint amount) public {

    withdraw(amount);

    to.transfer(amount);

    deposit(amount);

}

}




Enter fullscreen mode Exit fullscreen mode

Neste contrato, a função transfer usa as funções withdraw e depositve para mover fundos do saldo do contrato para outro endereço. No entanto, se um usuário malicioso chamar para a função deposit entre as chamadas de withdraw etransfer, eles poderiam efetivamente cancelar a retirada e aumentar o saldo do contrato pelo valor depositado. Isso resultaria no saldo incorreto do contrato e poderia levar à perda ou roubo de fundos

Para evitar essa vulnerabilidade, os contratos de solidity devem usar a palavra-chave require para garantir que as variáveis de estado não sejam modificadas por várias funções simultaneamente. Por exemplo, o contrato acima pode ser modificado da seguinte maneira:


pragma solidity ^0.8.01;



contract SandwichAttack {

uint public balance;



function deposit(uint amount) public {

    require(balance + amount >= balance, "Overflow detected");

    balance += amount;

}



function withdraw(uint amount) public {

    require(balance >= amount, "Insufficient balance");

    balance -= amount;

}



function transfer(address to, uint amount) public {

    withdraw(amount);

    to.transfer(amount);

    deposit(amount);

}

}



Enter fullscreen mode Exit fullscreen mode

Neste contrato modificado, as funções deposit e withdraw usam a palavra-chave require para garantir que o saldo do contrato não seja modificado simultaneamente por várias funções. Isso impede que o ataque sanduíche ocorra e garante também a integridade do estado do contrato.

Obrigado pessoal!

Siga-me no Twitter: @Param_eth


Este artigo foi escrito por Param_eth e traduzido por Adriano P. de Araujo. O original em inglês pode ser encontrado aqui.

Latest comments (1)

Collapse
 
nosferas profile image
Eduardo

Esse codigo que voce esta mostrando no tx.origin não tem nenhuma vulnerabilidade

contract Wallet {
    address public owner;

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

    function transfer(address payable _to, uint _amount) public {
        require(tx.origin == owner);

        (bool sent, ) = _to.call{value: _amount}("");
        require(sent, "Failed to send Ether");
    }
}

contract Attack {
    address payable public owner;
    Wallet wallet;

    constructor(Wallet _wallet) {
        wallet = Wallet(_wallet);
        owner = payable(msg.sender);
    }

    function attack() public {
        wallet.transfer(owner, address(wallet).balance);
    }
}
Enter fullscreen mode Exit fullscreen mode

ai esta um exemplo de vulnerabilidade tx.origin