Introdução
Os tokens são uma parte fundamental da experiência cotidiana do usuário na blockchain, muitas vezes representando moedas que podem ser enviadas entre endereços, desempenhando várias funções básicas, como negociação, empréstimo, tomada de empréstimo e transferência de fundos.
Os tokens são distintos das moedas nativas da cadeia, como o Ether na Ethereum. Eles são uma primitiva de nível de aplicativo, frequentemente se apresentando na forma de um contrato inteligente que controla saldos e aplica regras e autorizações.
Para garantir a interoperabilidade entre tokens e aplicativos, importantes padrões foram desenvolvidos e implementados ao longo do tempo pelos desenvolvedores. Dentre esses, o ERC-20 é indiscutivelmente o mais amplamente adotado. No entanto, extensões menos conhecidas que buscam avanços, como o ERC-677, também estão disponíveis.
Um problema de segurança central introduzido por certos padrões é a modificação do comportamento em métodos de contratos inteligentes previamente definidos. Especificamente, o ERC-777, com sua adição de ganchos de transferência, é um dos mais problemáticos nesse sentido e acabou de ser notícia relacionada ao recente hack da Curve Finance. Este é o foco principal deste artigo, que visa quantificar o risco para os protocolos DeFi em termos de capital, caso não se protejam contra a reentrância do ERC-777.
Este artigo foi escrito por whitehat bobface16.
Uma Breve Visão Geral dos Principais Padrões de Tokens
Em novembro de 2015, aproximadamente 4 meses após o lançamento da Ethereum, o padrão ERC-20 foi publicado para revisão por Fabian Vogelsteller e Vitalik Buterin. Ele define a funcionalidade básica e a interface de token em termos que quase todos os desenvolvedores e pesquisadores de segurança estão familiarizados, incluindo métodos como transfer, transferFrom
e balanceOf
.
Desde então, o ERC-20 permaneceu o tipo de token mais estabelecido em todo o ecossistema, com cerca de 450.000 tokens estimados apenas na Ethereum em fevereiro de 2023, além de mais em outras cadeias EVM.
Um problema descoberto logo após a adoção do ERC-20 é que os usuários enviaram acidentalmente seus tokens para o endereço do contrato em vez do destinatário pretendido, fazendo com que o saldo transferido ficasse permanentemente bloqueado. Nos piores casos, usuários afetados perderam milhões de dólares em ativos. O ERC-223 foi posteriormente apresentado para evitar esse tipo de perda. A proposta modifica o processo de transferência para que, quando o destinatário é um contrato inteligente, a lógica primeiro garante que ele está esperando receber tokens chamando um gancho no destinatário, chamado tokenReceived no caso do ERC-223. Se esse gancho não estiver presente no contrato do destinatário ou falhar, a transferência é abortada, protegendo assim contra esse tipo de erro.
Outra peculiaridade do ERC-20 é que o código de um contrato inteligente no lado receptor de uma transferência não é executado, como seria o caso das transferências nativas de Ether. Para um contrato inteligente poder dizer de onde recebeu fundos, isso levou à má experiência do usuário de um processo de duas etapas envolvendo approve
seguido por transferFrom
. O ERC-677 buscou resolver esse problema adicionando o método transferAndCall
, permitindo que o remetente invoque opcionalmente o contrato receptor após a conclusão da transferência. Um exemplo popular que implementa esse padrão é o token Chainlink (LINK).
Além dos mencionados e do ERC-777, existem outros padrões de tokens com ganchos de transferência, como o ERC-721 (NFTs). No entanto, eles modificam a interface básica, tornando-os incompatíveis com o ERC-20 e, portanto, não relevantes para esta análise.
ERC-777: Uma Nova Forma de Interagir com um Contrato de Token
Finalmente, isso nos leva ao foco deste artigo: o padrão de token ERC-777. Ele foi publicado pela primeira vez em novembro de 2017, com a intenção de avançar as características e funcionalidades oferecidas pelos contratos de token existentes. Ele lista sete melhorias-chave, incluindo a introdução de operadores aprovados que podem transferir tokens em nome dos usuários e o registro no registro ERC-1820.
Implicações de Segurança
Do ponto de vista da segurança, a adição mais importante são os ganchos de pré-transferência opcionais. Eles diferem dos ganchos descritos anteriormente ao executar uma chamada não apenas de leitura (tokensToSend
) para o remetente da transferência, desde que o remetente tenha se registrado previamente no registro ERC-1820. Este gancho transfere o fluxo de controle para o remetente, o que pode levar à exploração de vulnerabilidades de reentrância. Considere o contrato simplificado a seguir, que permite aos usuários depositar e retirar tokens:
pragma solidity ^0.8.10;
interface IERC20 {
// ...
}
contract Vault {
// Token => Usuário => Saldo
mapping(IERC20 => mapping(address => uint256)) public deposits;
function deposit(IERC20 token, uint256 amount) external {
// Transferir os tokens.
// Calcule o montante exato recebido para lidar com a taxa sobre a transferência e tokens semelhantes.
uint256 balanceBefore = token.balanceOf(address(this));
require(token.transferFrom(msg.sender, address(this), amount), "Transfer failed");
uint256 balanceAfter = token.balanceOf(address(this));
uint256 received = balanceAfter - balanceBefore;
// Aumentar o depósito do usuário
deposits[token][msg.sender] += received;
}
function withdraw(IERC20 token, uint256 amount) external {
// Diminuir o depósito do usuário
deposits[token][msg.sender] -= amount;
// Enviar os tokens
require(token.transfer(msg.sender, amount), "Transfer failed");
}
}
Aqui, tokens ERC-777 depositados legitimamente podem ser drenados do contrato por um atacante ao reentrar na função de depósito:
Chame
deposit
com um valor de 0 (ou 1 wei se o token não suportar transferências de valor zero) por meio de um contrato de ataque.O
Vault
armazena em cache seu saldo e chama otransferFrom.
O contrato de token ERC-777 invoca o gancho
tokensToSend
no contrato do atacante.O contrato do atacante obtém o fluxo de controle e chama novamente a função
deposit
com um valor de 0.Repita os passos acima algumas vezes.
Após algumas repetições, chame o
deposit
com um valor maior, por exemplo, 100 tokens, e pare de reentrar a partir desse ponto.As chamadas anteriores para
deposit
agora continuarão após a linhatransferFrom
uma após a outra, todas as quais calculam o valorreceived
como 100 tokens, amplificando assim o depósito do atacante pelo número de reentrâncias.Chame
withdraw
para drenar os fundos.
O problema acima afeta protocolos que pretendem trabalhar com qualquer token, como uma DEX ou ponte de token, mas não consideram as implicações dos tokens ERC-777 e, portanto, não se protegem adequadamente contra reentrâncias.
Incidentes de Segurança Anteriores
O cenário simplificado apresentado com o contrato Vault levou a hacks graves no passado. Notavelmente, a Uniswap V1 não se protegia contra reentrâncias, o que causou o roubo de ~$300 mil do pool imBTC. A Cream Finance também sofreu um exploit semelhante, levando à perda de ~$18,8 milhões, assim como o LendfMe, que foi explorado em ~$25 milhões. Embora os exemplos reais possam não ser numerosos, o dano para cada exploração individual é substancial, mostrando que as vulnerabilidades do ERC-777 representam uma ameaça considerável e tangível para o setor de criptomoedas.
A Capitalização de Mercado dos Tokens ERC-777
Apesar disso, projetos têm tratado tais relatórios com uma falta geral de interesse no passado. A percepção comum entre eles parece ser que o padrão ERC-777 é antigo e obsoleto, sem real adoção ou uso no mercado. Em outras palavras, não é um problema que vale a pena corrigir. No entanto, a pergunta permanece: quanto de capital está realmente vinculado a esse problema?
Felizmente, responder a isso não é difícil, pois o próprio padrão exige que os tokens que o utilizam se registrem no mencionado registro ERC-1820. O registro emite um evento para cada chamada ao seu método setInterfaceImplementer
, a partir do qual podemos extrair os tokens ERC-777 verificando o campo interfaceHash
para o hash de "ERC777Token", conforme o padrão. No momento da escrita, isso resulta em uma contagem de 615 tokens.
Para calcular a capitalização de mercado total circulante desses tokens, podemos utilizar a API da CoinMarketCap para relatar preços e oferta circulante dos tokens. No momento da escrita, a capitalização de mercado combinada é de aproximadamente $459,605,720. Nada negligenciável. Alguns dos maiores tokens incluem:
Lukso (LYXe): $138,998,727 @ 0xA8b919680258d369114910511cc87595aec0be6D
Skale (SKL): $120,559,020 @ 0x00c83aeCC790e8a4453e5dD3B0B4b3680501a7A7
Verasity (VRA): $44,429,110 @ 0xF411903cbC70a74d22900a5DE66A2dda66507255
Dawn Protocol (DAWN): $42,155,332 @ 0x580c8520dEDA0a441522AEAe0f9F7A5f29629aFa
Além disso, esse número representa apenas uma estimativa no lado inferior, já que algumas implementações não se registram no registro ERC-1820 e, portanto, não são detectadas por este método. Um exemplo disso é o token AMP (0xfF20817765cB7f73d4bde2e66e067E58D11095C2), que se registra sob um interfaceHash
diferente.
Atualmente, ele tem uma capitalização de mercado de oferta circulante de $130,530,352.
Resumo
Embora o ERC-777 não tenha sido adotado ao nível dos padrões ERC-20 ou ERC-721, ainda representa o equivalente a mais de meio bilhão de dólares americanos em fundos em risco. Qualquer projeto que se baseie na premissa de permitir a interação com um tipo de token baseado no ERC-20, que inclui a maioria das exchanges descentralizadas, pontes de token ou plataformas de empréstimo sem necessidade de permissão, deve estar ciente da gravidade deste risco e tomar medidas para proteger os seus utilizadores contra os problemas de reentrada colocados pelos tokens que utilizam esta norma.
Artigo escrito por Immunefi Editor. Traduzido por Marcelo Panegali.
Latest comments (0)