Prévia gratuita10 min de leitura

Reentrância: o truque mais antigo que ainda funciona

Um contrato que envia dinheiro antes de atualizar os próprios registros pode ser chamado de novo para enviar esse mesmo dinheiro, no meio do envio. Isso é reentrância, a falha por trás do primeiro grande desastre da história do Ethereum, e ela continua encontrando vítimas.

O mecanismo

  1. Um contrato guarda saldos de depositantes e oferece uma função de saque.
  2. O saque envia os fundos primeiro e só depois zera o saldo do depositante.
  3. Enviar fundos para outro contrato entrega o controle ao código desse contrato.
  4. Esse código chama o saque de novo. O saldo ainda não foi zerado, então a verificação passa uma segunda vez.
  5. O laço se repete até o pool ficar vazio.

A correção é uma regra de ordem conhecida como verificações, efeitos, interações: valide primeiro, atualize o seu próprio estado em segundo, fale com qualquer outra pessoa por último. Um guarda de reentrância, uma trava que recusa uma segunda entrada enquanto a primeira chamada está aberta, é o cinto que acompanha esse par de suspensórios.

Onde isso já aconteceu

  • The DAO, junho de 2016. Uma falha de reentrância drenou cerca de 3,6 milhões de ETH de um fundo de investimento construído no Ethereum. A resposta dividiu a rede: a maioria fez um fork para reverter o roubo, e a rede que se recusou continua até hoje como Ethereum Classic.
  • Curve Finance, julho de 2023. A falha não estava na lógica da própria Curve, mas em versões específicas do compilador Vyper, em que o guarda de reentrância não funcionava como estava escrito. Vários pools foram drenados, com perdas relatadas em torno de 70 milhões de dólares. Código auditado ainda pode ser desfeito pelas ferramentas que o compilam.

Por que ela sobrevive

  • Qualquer chamada externa pode entregar o controle: hooks de transferência de token, callbacks e padrões de token mais novos que notificam quem recebe.
  • A reentrância somente de leitura é mais sutil. Um atacante reentra em uma função de leitura enquanto o estado está atualizado pela metade, então outro protocolo lê um preço ou um saldo errado e age em cima disso.
  • A reentrância entre funções usa uma função diferente que compartilha o mesmo estado, então um guarda em uma função só não é suficiente.

O que realmente evita isso

  • Verificações, efeitos, interações, aplicado em todo lugar, não só no saque óbvio.
  • Guardas de reentrância em todo ponto de entrada que muda estado e faz chamadas externas.
  • Tratar todo token como código hostil, porque um contrato de token roda a própria lógica durante uma transferência.
  • Testes que chamam de volta o contrato a partir de um receptor malicioso, que é como isso é encontrado antes do lançamento.
Educação, não aconselhamento financeiro. Nada aqui é uma recomendação para usar ou evitar qualquer protocolo.
Reentrância: o truque mais antigo que ainda funciona · Crypto Academy