Encontre o Bug de Reentrância: Checks-Effects-Interactions em Solidity

Segurança de Criptomoedas & Blockchain Nível 4/4 ~7 min 16 de agosto de 2026

O desafio

Este cofre Solidity envia ether ao chamador antes de atualizar o saldo do chamador. Um contrato malicioso pode chamar de volta durante essa brecha e esvaziar o cofre. Leia o contrato e digite o nome da função com essa falha.

O que você vai aprender

  • Reconhecer uma chamada externa colocada antes de uma atualização de estado como um ponto de reentrância
  • Aplicar o padrão checks-effects-interactions para auditar uma função
  • Explicar como um fallback malicioso reentra em um contrato para drenar fundos
  • Diferenciar uma função segura que só atualiza estado de uma que faz uma chamada externa
  • Nomear as correções corretas: reordenar efeitos antes das interações ou usar um guarda

Habilidades testadas

Auditoria de contratos inteligentesRevisão de código-fonte SolidityAnálise de reentrância

Pré-requisitos

  • Sintaxe básica de Solidity (mappings, msg.sender, call)
  • Como transferências de ether podem invocar uma função fallback
  • O que é o estado de um contrato (balances)

Como funciona

Reentrância é a vulnerabilidade clássica de contratos inteligentes por trás de algumas das maiores perdas em DeFi. Ela acontece quando um contrato faz uma chamada externa antes de terminar de atualizar seu próprio estado. A chamada externa pode passar o controle para um contrato atacante, que chama de volta a função original antes de o estado ter sido definido - assim o contrato age sobre dados desatualizados.

Em Vault.sol, withdraw()bal = balances[msg.sender], depois transfere o ether com msg.sender.call{value: bal}(""), e só depois define balances[msg.sender] = 0. A chamada de baixo nível call aciona a função fallback do destinatário. O fallback de um contrato malicioso simplesmente chama withdraw() de novo - e como o saldo ainda não foi zerado, a verificação require(bal > 0) ainda passa e ele é pago de novo. Repetir esse loop esvazia todo o cofre.

O princípio violado é o checks-effects-interactions: primeiro fazer as verificações (require), depois aplicar os efeitos (atualizar os saldos), e só então realizar as interações (chamadas externas). deposit() segue isso trivialmente - atualiza o estado e não faz nenhuma chamada externa, então é seguro e serve como distrator. A função vulnerável é withdraw, e a habilidade de auditoria é perceber que a atualização de estado vem depois da chamada.

Erros comuns

  • Apontar deposit(). Ela só atualiza estado e não faz nenhuma chamada externa, então não pode sofrer reentrância.
  • Culpar msg.sender.call de forma genérica. A chamada em si está correta; o bug é sua posição antes da atualização do saldo. Nomeie a função, não apenas a chamada.
  • Supor que as verificações de overflow do Solidity 0.8 ajudam. Aritmética segura não previne reentrância - a ordem das operações é que previne.
  • Achar que require(bal > 0) é a proteção. Ela volta a passar em cada reentrada porque o saldo ainda é diferente de zero durante a chamada.

Como se proteger

Sempre termine de atualizar o estado antes de fazer uma chamada externa. Reordene withdraw() para que o saldo seja zerado primeiro, e adicione uma proteção extra em profundidade.

  • Siga o padrão checks-effects-interactions: defina balances[msg.sender] = 0 antes do call.
  • Use um guarda de reentrância (por exemplo, o modificador nonReentrant da OpenZeppelin) em funções que movimentam fundos.
  • Prefira padrões de pull-payment em vez de enviar ether dentro de lógica complexa.
  • Audite toda call/transfer externa em busca de estado que ainda seja mutável quando o controle sai do contrato.

Solução completa

Membros Pro e Max desbloqueiam o passo a passo completo.

Assinar Pro

Estatísticas da comunidade

111 resoluções
79% taxa de sucesso
M2F14M3 Primeiro sangue

Hacks de hoje relacionados

24.000+ Hackers 100+ Labs & Cursos Grátis
Comece Grátis