O que diabos é Mythril?
Não é um minério raro de um romance de fantasia nem de um conjunto de armadura de videogame, mas uma das mais poderosas ferramentas de segurança de contrato inteligente, permitindo detectar vulnerabilidades no bytecode para muitas Blockchains compatíveis com EVM, como Ethereum, Vechain, Hedara , Roostock e Tron.
Mythril faz parte das principais ferramentas do Consensys Mythx, um dos maiores serviços de segurança de contrato inteligente para Ethereum, cujo principal objetivo é garantir que as equipes de desenvolvimento evitem erros dispendiosos e tornem o Ethereum mais seguro e confiável… ou pelo menos é o que sua página diz.
Esta informação é ótima, mas…
Por que você deveria se preocupar com isso?
Bem, de acordo com a Chainalysis, uma empresa confiável de análise de blockchain, as violações de segurança levaram quase 1,4 bilhão de dólares a serem roubados no primeiro semestre de 2022, o que é uma loucura se você pensar bem. Existem vários exemplos, como Wormhole e Poly Network com mais de $ 320 milhões em perdas ou o maior deles foi o hack da Ronin Network com mais de $ 600 milhões em ativos comprometidos, e o último em agosto para Nomad Protocol com cerca de duzentos milhões de dólares em perdas.
Então, como você pode ver, proteger nossos contratos inteligentes é muito importante. Além disso, a maioria desses hacks aconteceu devido a erros humanos e falta de medidas de segurança nos contratos inteligentes, portanto a prevenção é fundamental, mas não é tão simples e é realmente fácil ficar confuso porque há muitos problemas, vulnerabilidades e ataques como:
- Ataques de reentrância
- Overflows e Underflows aritméticos
- Problemas com Delegate Call
- Ataques de endereço curto
- Ataques de negação de serviço (DOS)
- Ataques de ataque!
Ok, talvez o último não, mas você entendeu. De qualquer forma, com o tempo você se tornará um desenvolvedor mais experiente, entenderá melhor todos esses problemas e poderá escolher a abordagem correta para cada um. No entanto, se você não é tão experiente, os problemas não são tão óbvios.
Vejamos um exemplo, aqui temos um contrato como este em que você pode depositar e sacar algum dinheiro, pode ser para uma campanha de crowdfunding.
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ReentrânciaIssue {
mapping(address => uint) public balances;
function deposit() public pay {
saldos[msg.sender] += msg.valor;
}
função retirar(uint _amount) public {
require(saldos[msg.sender] >= _amount);
(bool sucesso, ) = msg.sender.call{valor: _amount}("");
requer(sucesso);
saldos[msg.sender] -= _amount;
}
function getBalance() public view return (uint) {
return address(this).balance;
}
}
Se rodarmos esse código com nosso framework parece que tudo roda bem, então está tudo certo né? Temos um contrato inteligente funcional e bom. Bem, na verdade não, este contrato é vulnerável a ataques de reentrância que permitem amplamente que um hacker em potencial execute uma função Fallback para retirar os fundos repetidamente sem atualizar o estado do contrato.
Então, basicamente, o invasor envia um endereço que não é uma carteira, mas outro contrato, e o contrato executa algum código arbitrário que rouba todos os fundos do contrato principal. Isso não é nada óbvio! Especialmente se você está iniciando sua jornada como desenvolvedor de contratos inteligentes, como podemos estar cientes desses problemas?
Em primeiro lugar, você deve sempre procurar mais informações e recursos educacionais para aprender sobre esses problemas, para se tornar um desenvolvedor melhor com o tempo, enquanto isso? Podemos usar algumas ferramentas, então vamos dar uma olhada em Mythril.
Instalando o Mythril
Para usar esta ferramenta você precisa de alguns pré-requisitos já instalados como python, brownie, NPM e um Terminal Unix (WSL) neste caso. Se você já os possui, pode instalar o Mythril, mas se não tiver nenhum ou alguns deles, não se esqueça de verificar o artigo relacionado ao repo do Github onde você pode encontrar mais instruções sobre como instalar as dependências e também os comandos para executar o Mythril.
O processo para qualquer terminal baseado em Unix é bem direto, você só precisa de alguns comandos em seu terminal e estará pronto para começar.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
pip install mythril
Usando Mythril
Assim que o processo de instalação estiver concluído, podemos continuar, então vamos analisar nosso contrato inteligente para encontrar vulnerabilidades, então, usando o seguinte comando, podemos solicitar que Mythril comece a analisar nosso contrato.
myth analyze contracts/VulnerableContracts/ReentrancyIssue.sol
Esse processo pode levar de alguns segundos a vários minutos e nos dá a seguinte resultado:
- A ID do problema.
- Qual função está causando o problema.
- A quantidade de gas usado.
- Uma explicação detalhada da vulnerabilidade, neste caso estamos permitindo enviar um endereço externo ao contrato como parâmetro, e esse endereço pode ser outro contrato que é quem executa o ataque.
- A linha exata em que o problema ocorre.
- Toda a sequência de execução.
Isso é ótimo, porque se não sabíamos sobre esse problema antes, agora podemos corrigir nosso contrato inteligente para ser mais robusto e seguro.
Portanto, a maneira mais fácil de fazer isso é adicionar um modificador que bloqueará a função até que ela termine o processamento, portanto, mesmo que o invasor queira usar essa função de fallback repetidamente, como a função é bloqueada até que esta linha seja concluída, agora estamos seguros contra ataques de reentrância, então o contrato corrigido com se parece com isto:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ReentrancyGuarded {
mapping(address => uint) public balances;
bool internal _inCall;
// ESTA LINHA!!
modifier nonReentrant() {
require(!_inCall, "ReentrancyGuard: reentrant call");
_inCall = true;
_;
_inCall = false;
}
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint _amount) public nonReentrant {
require(balances[msg.sender] >= _amount);
(bool success, ) = msg.sender.call{value: _amount}("");
require(success);
balances[msg.sender] -= _amount;
}
function getBalance() public view returns (uint) {
return address(this).balance;
}
}
Vejamos outro exemplo, aqui temos um contrato com uma versão antiga de solidity destinada a ser um local de compra e venda de tokens.
//SPDX-License-Identifier: MIT
pragma solidity ^0.4.21;
contract IntegerVulnerability {
mapping(address => uint256) public balanceOf;
uint256 constant PRICE_PER_TOKEN = 1 ether;
function sale(address _player) public payable {
require(msg.value == 1 ether);
}
function isComplete() public view returns (bool) {
return address(this).balance < 1 ether;
}
function buy(uint256 numTokens) public payable {
require(msg.value == numTokens * PRICE_PER_TOKEN);
balanceOf[msg.sender] += numTokens;
}
function sell(uint256 numTokens) public {
require(balanceOf[msg.sender] >= numTokens);
balanceOf[msg.sender] -= numTokens;
msg.sender.transfer(numTokens * PRICE_PER_TOKEN);
}
}
Depois de executar a ferramenta myth analyze
, descobrimos que podemos ter problemas com nossas variáveis uint
devido a transbordamentos, portanto, se uma variável atingir sua capacidade máxima, ela será padronizada novamente para 0 (incluindo outros possíveis problemas).
A solução mais fácil para isso é apenas usar a versão mais recente do solidity, que gerencia os transbordamentos automaticamente e você não precisa de algo como o Safe Math.
pragma solidity ^0.8.0;
E quanto a outros tipos de vulnerabilidades que não são tão comuns e talvez nunca tenhamos ouvido falar antes? Este contrato possui uma funcionalidade de autodestruição, que somente o proprietário do contrato deve ser capaz de executar.
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SelfDestruct {
address owner;
mapping(address => uint256) balances;
bytes11 private backdoorpwd = "cryptocrome";
bool private passwordentered = false;
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function.");
_;
}
constructor() {
owner = msg.sender;
}
function kill() public onlyOwner {
selfdestruct(payable(owner));
}
function activateBackdoor(bytes11 _password) public {
require(_password == backdoorpwd, "Wrong password");
passwordentered = true;
}
function pwnContract() public {
require(passwordentered == true, "Backdoor not activated");
owner = msg.sender;
}
}
Então rodamos a ferramenta do analisador, desta vez com 3 transações porque esse é o valor mínimo para destruir um contrato.
myth analyze contracts/VulnerableContracts/SelfDestruct.sol -t3
E receberemos a informação de que nossa autodestruição não está protegida, e você sabe que eu não sei o que isso significa, mesmo com a descrição aqui.
É por isso que o ID é importante, porque podemos apenas pesquisar no Google. E há páginas úteis como esta com informações realmente úteis sobre a vulnerabilidade e sua possível correção. Também recomendo ler as outras vulnerabilidades nesta página para melhorar sua compreensão do Solidity.
Depois de entender qual era a vulnerabilidade, descobrimos que o problema é porque temos uma senha facilmente quebrável, de forma que qualquer pessoa pode usar a função de autodestruição sem permissão. Portanto, não devemos usar algo assim em nossos contratos.
Mais um problema resolvido, legal… não é?
Contratos com importações
Que tal mais contratos com importações, como este em que usamos o OpenZeppelin.
//SDPX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
contract MetamorphicVulnerability is Initializable {
address payable owner;
function kill() external {
require(msg.sender == owner);
selfdestruct(owner);
}
}
Ainda podemos analisá-lo, mas damos alguns passos extras, Mythril analisa o bytecode
dos contratos compilados e, como se trata de uma importação externa, se executarmos assim, vai nos gerar um erro. A solução é criar um arquivo json com um remapeamento apontando o pacote nos módulos do nó, e claro que precisaremos adicionar esse pacote usando o NPM
.
{
"remappings": ["@openzeppelin=node_modules/@openzeppelin"]
}
Instalando o OpenZeppelin.
npm install @openzeppelin/contracts
Bom e simples, agora podemos executar o analisador apenas apontando para os remapeamentos.
myth analyze contracts/VulnerableContracts/MetamorphicVulnerability.sol
Uma ferramenta para governar todos eles?
GIF
Portanto, Mythril é a ferramenta definitiva para auditoria de contratos inteligentes? Bem, na verdade não, existem alguns cenários em que o Mythril não é bom o suficiente para detectar algumas vulnerabilidades, por exemplo no último contrato com importações ele tem uma vulnerabilidade em que o contrato pode não ser inicializado e, como podemos ver no resultado do analisador, Mythril pensa que está tudo ok.
Resumo
Em conclusão, Mythril não é perfeito, mas é uma ferramenta realmente poderosa que todo desenvolvedor de contrato inteligente deve conhecer, é fácil de usar e nos fornece resultados detalhados. Sem mencionar que é de código aberto e compatível com EVM. Minha recomendação final é, mesmo que você tenha feito suas diligências e autoauditado seus contratos, se você for implantar um protocolo que irá lidar com ativos reais, ainda assim recomendo que você contrate um serviço de auditoria externa, lembre-se que somos todos humanos e pode haver problemas que não percebemos e ter um ponto de vista externo é sempre importante.
Com isso dito, isso é tudo para ler este artigo até o fim e não se esqueça de verificar o Github … bons testes.
Este artigo foi escrito por Vasiliy Gualoto e traduzido por Diogo Jorge. O artigo original pode ser visto aqui.
Latest comments (0)