Introdução
Dada a prosperidade do ecossistema NFT na Ethereum e o aumento dos problemas de segurança relacionados, publicaremos uma série de blogs para apresentar esses problemas de segurança e dar algumas sugestões para os desenvolvedores garantirem contratos. Neste blog, apresentaremos o problema de reentrância em contratos NFT e como mitigar a vulnerabilidade relacionada.
O que é Reentrância
De acordo com a Wikipédia
Em computação, um programa de computador ou sub-rotina é chamado de reentrante se várias invocações podem ser executadas simultaneamente em vários processadores, ou em um sistema de processador único, onde um procedimento reentrante pode ser interrompido no meio de sua execução e então ser chamado novamente com segurança (“re-entrada”) antes que suas invocações anteriores concluam a execução.
Em um contrato inteligente, a reentrância pode ocorrer quando uma função executa uma chamada externa de outro contrato, que invoca ainda mais a função original (ou outras funções) antes que a invocação original retorne. Se a chamada externa for controlada por uma entidade não confiável (por exemplo, um contrato malicioso), pode levar a um resultado inesperado. Isso porque, em algumas funções, o resultado depende do estado do contrato. O estado do contrato será atualizado após a chamada externa. No entanto, a invocação reentrante da função usará o estado antigo em vez do atualizado.
A reentrância tem sido um problema predominante em contratos inteligentes, por exemplo, o ataque MakerDAO, o ERC777 na Uniswap e outros (pesquise Reentrancy (Reentrância) na BlockSec Academy).
Reentrância em NFT
Para contratos NFT, existem algumas chamadas de funções externas implícitas que podem ser negligenciadas pelos desenvolvedores. Elas incluem a função onERC721Received
e onERC1155Received
. A função onERC721Received
foi projetada para verificar se o contrato receptor pode lidar com NFTs (para evitar que as NFTs sejam bloqueadas para sempre). Essa função é invocada no safeTransferFrom
e _safeMint
do contrato ERC721. O similar existe no contrato ERC1155. Devido a essas chamadas de funções externas, a reentrância pode acontecer sem ser notada pelos desenvolvedores do contrato.
Reentrância de Função Única
A reentrância de função única é a forma mais simples de ataque de reentrância. Nesse tipo de ataque de reentrância, a função reinvocada é a mesma da função original. Um invasor pode invocar a função repetidamente antes que a primeira invocação da função seja concluída. Nos contratos NFT, isso geralmente acontece nas funções relacionadas à operação mint
.
Por exemplo, alguns projetos de NFT podem dar a cada usuário a chance de criar NFT livremente, definir o fornecimento máximo de todo o projeto ou definir a quantidade máxima de NFTs que um usuário pode manter. Normalmente, essas restrições são verificadas antes da operação de cunhagem real. Mas se os estados relacionados a essas restrições forem atualizados após a função safeMint
, o invasor poderá reinserir essa função mint e ignorar as restrições, pois os estados relacionados são os mesmos da primeira invocação dessa função. O ataque de reentrância do HypeBears postado em nosso blog anterior é um exemplo.
Uma reentrância de função única mais complicada ocorre quando a função safeMint
é usada em um loop e as restrições são verificadas antes do início do loop. Nesse cenário, mesmo alguns estados serão atualizados automaticamente antes da chamada externa, as chamadas de função safeMint
restantes no loop ainda podem ignorar a validação, pois o loop já foi iniciado e a validação acontece antes do início do loop.
Por exemplo, no exemplo mostrado em outro post, a função mintNFT
verificará se o número de NFTs que o usuário deseja cunhar mais o fornecimento atual pode exceder o fornecimento máximo. A função safeMint
atualiza o fornecimento total antes da chamada externa onERC721Received
. Um invasor ainda pode explorá-lo, pois a função safeMint
só aumenta a oferta total em 1 a cada vez. Portanto, se o invasor reinserir a função mintNFT
na primeira chamada de função safeMint
do loop, a oferta total se tornará a oferta antiga mais 1, em vez da oferta antiga mais a quantidade de NFTs que serão cunhadas pela primeira chamada de mintNFT
.
Reentrância entre funções
Em vez de inserir a mesma função, o invasor pode inserir outra função que compartilhe ou dependa dos estados com a função de origem. Detalhamos alguns casos em nossos blogs anteriores para o caso Revest e o caso OMNI.
Resumo e sugestões
Aqui estão algumas sugestões para desenvolvedores de contratos inteligentes NFT para mitigar a ameaça de reentrância.
- Use o padrão Checks-Effects-Interactions em seu código.
- Tenha cuidado ao usar qualquer biblioteca de terceiros que introduzirá chamadas externas. Para contratos NFT, tenha cuidado com o retorno de chamada implícito da função
onERC721Received
eonERC1155Received
.
Sobre nós
A equipe BlockSec se concentra na segurança do ecossistema blockchain e na pesquisa de monitoramento e bloqueio de hacks de criptomoedas, auditoria de contratos inteligentes.
Saiba mais sobre a BlockSec:
Twitter: https://twitter.com/BlockSecTeam
Github Academy: https://github.com/blocksecteam/blocksec_academy
Artigo escrito por BlockSec e traduzido por Marcelo Panegali
Latest comments (0)