Se você estiver trabalhando com contratos inteligentes — ou apenas explorando-os — provavelmente já sabe que a segurança do contrato inteligente é importante. Os contratos inteligentes são imutáveis uma vez implantados e geralmente envolvem quantias significativas de dinheiro. Escrever um código seguro e confiável antes da implantação deve ser uma prioridade. E à medida que a adoção da blockchain acelera, garantir a segurança dos contratos inteligentes torna-se ainda mais importante.
Um dos melhores incrementos à sua auditoria de contrato inteligente é o Fuzzing, uma técnica de teste dinâmico que expõe vulnerabilidades gerando e injetando entradas aleatórias em seus contratos inteligentes durante o teste.
Neste artigo, exploraremos como usar o Fuzzing para auditar efetivamente um contrato inteligente. Especificamente, veremos o ConsenSys Diligence Fuzzing — uma nova solução do Fuzzing as a service (FaaS ou Fuzzing como um serviço). Vamos nos aprofundar nos aspectos técnicos e mostrar alguns exemplos de código.
O que é o Fuzzing?
O Fuzzing é uma técnica de teste dinâmico em que entradas aleatórias (ou semi-aleatórias) chamadas “Fuzz” são geradas e injetadas no código. O Fuzzing pode ajudar a revelar erros e vulnerabilidades que não foram detectados pelos métodos de teste tradicionais.
Os testes manuais (unitários) exigem que você descubra qual funcionalidade testar, quais entradas usar e qual deve ser a saída esperada. É demorado, difícil e, no final, ainda é fácil perder cenários.
Por outro lado, o Fuzzing (ou teste Fuzz) é um processo de teste automatizado que envia dados aleatórios para um aplicativo para testar sua segurança. Um Fuzzer pode ajudá-lo a entender como um programa responde a entradas imprevisíveis.
O Fuzzing já existe há algum tempo. O Defensics e a Burp Suite são alguns exemplos no mundo do desenvolvimento tradicional. Existem também várias ferramentas Fuzzing Web3/blockchain disponíveis, como o Echidna e o Foundry. No entanto, o Diligence Fuzzing é o Fuzzing as a service e torna tudo um pouco mais simples de implementar, o que, no final, significa melhores auditorias e contratos mais seguros. Então, vamos analisá-lo com mais detalhes.
ConsenSys Diligence Fuzzing
O Diligence Fuzzing (da ConsenSys, que também está por trás dos padrões do ecossistema, como a MetaMask e o Infura) é um Fuzzer criado para contratos inteligentes da Web3. Ele:
- Funciona a partir de uma especificação formal que descreve o comportamento esperado de seu contrato inteligente.
- Gera sequências de transações que podem violar suas asserções.
- Usa análise avançada para encontrar entradas que cobrem uma quantidade máxima de seu código de contrato inteligente.
- Valida a lógica de negócios do aplicativo e verifica a correção funcional.
- Fornece a você todas as descobertas.
E tudo como um serviço com o mínimo de trabalho de sua parte!
Para usar o Diligence Fuzzing, siga estas três etapas:
- Primeiro, defina suas especificações de contrato inteligente usando o Scribble.
- Em seguida, envie o código para o Diligence para executar seu Fuzzing.
- Por fim, com o relatório de auditoria, corrija e melhore seu código!
Fuzzing em Ação
Então, vamos testá-la e vê-la em ação. Usaremos o Fuzzing CLI e o Scribble para testar um contrato inteligente de amostra.
Etapa 1: Inscreva-se
Primeiro, inscreva-se para ter acesso ao Diligence Fuzzing.
Etapa 2: Instalar as dependências
Em seguida, instale o Fuzzing CLI e o Scribble. A ConsenSys recomenda que você tenha as versões mais recentes do Node e o Python. Certifique-se de estar usando pelo menos o Python 3.6 e o Node 16. Em seguida:
pip3 install diligence-fuzzing
npm i -g eth-scribble ganache truffle
Nota: isso requer um subsistema Linux, mac ou Linux com Windows. O Windows Powershell tem algumas complexidades nas quais a equipe está trabalhando. Você sempre pode usar um espaço de código do Github (que cria uma interface semelhante ao VScode com uma compilação inicializada limpa) e instalar os pré-requisitos acima por meio da linha de comando.
Etapa 3: Obter uma chave de API
Agora você precisa gerar uma chave de API para usar a CLI. Visite a página Chaves de API e clique em Create new API Key (Criar nova chave de API).
Etapa 4: Definir a configuração do Fuzzing
Agora precisamos de um contrato inteligente para Fuzz! Como parte de seu próprio tutorial, a ConsenSys fornece um exemplo de contrato inteligente para usar. Vamos usar apenas esse.
git clone https://github.com/ConsenSys/scribble-exercise-1.git
Abra o arquivo .fuzz.yml
do projeto e adicione sua chave de API para a propriedade “key” na linha 25.
# .fuzz_token.yml
fuzz:
# Informe à CLI onde encontrar os contratos compilados e os artefatos de compilação
build_directory: build/contracts
# O endereço a seguir será o principal alvo da campanha do Fuzzing
deployed_contract_address: "0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab"
# Faremos o Fuzzing com 2 núcleos 🚀
number_of_cores: 2
# Execute a campanha por apenas 3 minutos.
time_limit: 3m
# Coloque a campanha no projeto Sribble Exercício 1
project: "Scribble Exercise 1"
# Quando a campanha for criada, ela receberá um nome <prefix>_<random_characters>
campaign_name_prefix: "campanha ERC20"
# Aponte para seu nó Ganache que contém a semente 🌱
rpc_url: "http://localhost:8545"
key: "INSIRA SUA CHAVE DE API AQUI"
# Este é o contrato para o qual a campanha mostrará a cobertura/mapeamento de problemas etc.
# É uma lista de todos os contratos relevantes (não se preocupe com as dependências, nós as obteremos automaticamente 🙌)
targets:
- "contracts/vulnerableERC20.sol"
Nota: Certifique-se de interromper suas campanhas Fuzzing ou definir um limite de tempo, ou pode ser executado por um tempo inesperadamente longo. Você notará no arquivo acima que definimos o limite de tempo para nossas campanhas em três minutos.
Etapa 5: Definir as propriedades do Fuzzing
Observe também que temos nosso contrato inteligente:
contracts/vulnerableERC20.sol.
Em seguida, precisamos definir as propriedades que queremos que o Fuzzer verifique no contrato inteligente. Usaremos o Scribble para esta etapa. O Scribble é uma linguagem de especificação que traduz especificações de alto nível em código Solidity. Ele permite que você anote seus contratos com propriedades e, em seguida, transforme essas anotações em afirmações concretas que podem ser usadas por ferramentas de teste (como o Diligence Fuzzing). Muito legal!
Adicionaremos os segmentos de código destacados ao nosso contrato:
pragma solidity ^0.6.0;
/// #invariant "os saldos estão em sincronia"
unchecked_sum(_balances) == _totalSupply;
contract VulnerableToken {
Essa anotação garantirá que nosso suprimento total e saldos estejam sincronizados.
Etapa 6: Executar
Agora nós vamos executar a Fuzz! Basta executar este comando:
make fuzz
Etapa 7: Avaliar os resultados
Depois que o Fuzzer estiver pronto (pode levar um ou dois minutos para inicializar), podemos obter nossos resultados. Podemos usar o link que o Fuzzer nos fornece ou podemos acessar nosso painel.
Olhando para as propriedades, podemos ver o que está sendo Fuzzed e quaisquer violações. E adivinha? Encontramos um erro! Clique no botão de localização da linha para ver o código ofensivo.
Para obter detalhes, clique em Show transaction details (Mostrar detalhes da transação). Podemos ver o Fuzzer chamado “transfer”:
Após um exame mais detalhado, agora podemos ver o que causou nosso erro.
Os argumentos transfer_to e origin são os mesmos. Deve haver uma vulnerabilidade de segurança quando alguém envia tokens para si mesmo. Vamos examinar o código-fonte para ver o que há de errado.
function transfer(address _to, uint256 _value) external returns (bool) {
address from = msg.sender;
require(_value <= _balances[from]);
uint256 newBalanceFrom = _balances[from] - _value;
uint256 newBalanceTo = _balances[_to] + _value;
_balances[from] = newBalanceFrom;
_balances[_to] = newBalanceTo;
emit Transfer(msg.sender, _to, _value);
return true;
}
Sim! Podemos ver que, quando o remetente e o destinatário são iguais, as linhas 30 e 31 ficam um pouco estranhas — uma está alterando o valor da conta 'from' e a outra está alterando o valor da conta 'to'. O código assume que são contas diferentes. Mas como é a mesma conta, quando chegamos à linha 31, o valor que temos não é o valor que esperamos. Já foi alterado pela linha anterior.
Podemos corrigir isso adicionando as linhas de código destacadas abaixo:
function transfer(address _to, uint256 _value) external returns (bool) {
address from = msg.sender;
require(_value <= _balances[from]);
_balances[from] -= _value;
_balances[_to] += _value;
uint256 newBalanceFrom = _balances[from] - _value;
uint256 newBalanceTo = _balances[_to] + _value;
_balances[from] = newBalanceFrom;
_balances[_to] = newBalanceTo;
emit Transfer(msg.sender, _to, _value);
return true;
}
Aqui estão vários outros detalhes técnicos a serem observados:
- O script seed.js faz algum trabalho de configuração para você aqui. Ele implementa o contrato em um nó de teste. Ele também pode fazer coisas como cunhagem de tokens, abrir pools, etc. Ele dá ao Fuzzer o estado correto para iniciar.
- O arquivo yml tem muitos parâmetros de configuração que você pode explorar. Em especial o endereço do contrato para o Fuzzing, a chave de API, o time_limit para o Fuzzing e alguns outros.
- A CLI vem com um gerador de configuração automática. Execute Fuzz generate-config para obter algumas perguntas e respostas úteis para gerar a configuração.
Auditorias de Contratos Inteligentes — Use o Fuzzing!
O Fuzzing e o Diligence Fuzzing-as-a-service são ferramentas poderosas para testar a auditoria de contratos inteligentes da blockchain Ethereum. Esteja trabalhando em finanças descentralizadas (DeFi), NFTs ou apenas começando no desenvolvimento de contratos inteligentes, isso pode levá-lo ao próximo nível de identificação e correção de vulnerabilidades em seus contratos inteligentes. Juntamente com revisões manuais, testes de unidade, testes manuais, testes de penetração, revisões de código e muito mais, o Fuzzing deve ser uma parte fundamental do seu processo de auditoria de segurança de contrato inteligente para uma base de código mais segura e robusta.
Tenha um ótimo dia!
Artigo escrito por John Vester. Traduzido por Panegali
Top comments (0)