Comprar Cripto
Mercado
Spot
Futuros
Finanças
Promoção
Mais
Zona de Iniciantes
Fazer Login
Relatório de análise Detalhes
Pesquisas sobre a Indústria

Uma Introdução às Vulnerabilidades e Ataques Comuns em Contratos Inteligentes

Publicado em 2024-02-14

O Que É um Contrato Inteligente?

O Ethereum tem dois tipos comuns de contas: Contas de Propriedade Externa (EOA) e Contas de Contrato Inteligente (SCA).

As EOA são muito semelhantes às contas financeiras eletrónicas que usamos comumente para armazenar fundos e interagir com aplicações. Por exemplo, os utilizadores depositam moeda fiduciária através do PayPal e interagem com vários websites, lojas e aplicações para pagamentos. Os mineradores de DeFi geralmente armazenam criptomoedas nas suas EOA, interagem com dApps DeFi e depositam fundos em dApps para obter lucros. No entanto, as EOA têm uma característica que as contas financeiras eletrónicas não possuem: os utilizadores devem ter o seu controlo sobre as EOA verificado através da propriedade de chaves privadas — não são suas chaves, não são suas moedas.

As SCA são também um tipo de conta que está essencialmente associada a um segmento de bytecode executável (também conhecido como contrato inteligente). O contrato inteligente descreve várias lógicas de negócio e serve como backend para dApps. No entanto, apesar de terem mais restrições em comparação com as linguagens de desenvolvimento Turing completas tradicionais, os contratos inteligentes quase-Turing completos ainda têm sido vulneráveis a numerosos ataques, causando inúmeros danos à indústria blockchain.

Ataques Comuns a Contratos Inteligentes

1. Ataque de Reentrada

O ataque mais comum e notório é o ataque de reentrada, que foi responsável pela bifurcação do Ethereum que levou à criação do Ethereum Classic. Em 2016, hackers executaram um ataque de reentrada no contrato The DAO, roubando 3.600.000 ETH avaliados em mais de 150 milhões de dólares na época. Este ataque, ocorrendo durante os estágios iniciais do Ethereum, devastou o ecossistema e abalou a confiança dos investidores, levando finalmente a uma bifurcação.

Lógica Específica

Aqui está um exemplo para ajudar a entender melhor o princípio do ataque de reentrada. O Banco B emprestou anteriormente algum dinheiro ao Banco A. Um dia, o Banco B inicia uma transferência para o Banco A, solicitando a transferência de todo o dinheiro de volta para o Banco B. O caminho normal é o seguinte:

Passo 1: O Banco B solicita a retirada de fundos

Passo 2: O Banco A transfere os fundos para o Banco B

Passo 3: O Banco A confirma a transferência bem-sucedida para o Banco B

Passo 4: O Banco A atualiza o saldo da conta do Banco B.

No entanto, se o Banco B criar uma brecha após o Passo 2 e continuar a solicitar todo o dinheiro do Banco A sem confirmação no Passo 3, então o saldo da conta do Banco A no Banco B permanecerá inalterado. Esta chamada recursiva esvaziará todos os ativos do Banco A.


Contratos Inteligentes Relacionados

O contrato do Banco A inclui duas funções:

  • deposit(): Uma função de depósito que deposita dinheiro no Banco A e atualiza o saldo do usuário;
  • withdraw(): Uma função de saque que permite aos usuários retirar todos os seus fundos do Banco A.
  • O contrato de ataque do Banco B envolve principalmente um loop que aciona a função de callback receive(), que por sua vez chama a função withdraw() do contrato do Banco para drenar os ativos do Banco A através de uma sequência de 1 depósito, 1 saque e chamadas da função de callback receive(), e finalmente atualiza o saldo de B em A. Inclui duas funções:receive(): Uma função de callback acionada quando ETH é recebido, que chama recursivamente a função withdraw() do contrato do Banco para fazer saques.
  • attack(): Primeiro chama a função deposit() do contrato do Banco para atualizar o saldo e depois a função withdraw() para iniciar o primeiro saque, e aciona a função de callback receive() para chamar recursivamente withdraw() para drenar os ativos do contrato do Banco.


Solução

Implementando um bloqueio de reentrada

Um bloqueio de reentrada é um modificador utilizado para prevenir a reentrada, assegurando que uma chamada deve completar sua execução antes que possa ser invocada novamente. Por exemplo, uma vez que o ataque pelo Banco B requer chamar a função withdraw() do contrato do Banco múltiplas vezes, ele falhará com a implementação de um bloqueio de reentrada.

Como Utilizá-lo

2. Uso indevido de tx.origin

A função principal de tx.origin num contrato inteligente é recuperar a conta original que iniciou a transação. Aqui, discutiremos duas variáveis comuns em contratos inteligentes: msg.sender e tx.origin . msg.sender recupera a conta que está a chamar diretamente o contrato inteligente, enquanto no mundo da blockchain, devido às chamadas aninhadas e mútuas de diferentes contratos inteligentes (como o DeFi Lego), tx.origin é necessário para obter a conta original que iniciou a transação. Surge uma vulnerabilidade quando os desenvolvedores de dApps apenas verificam a segurança de tx.origin no código, negligenciando a verificação de segurança de atacantes que implementam contratos intermediários para contornar tx.origin e lançar ataques.

Lógica Específica

Aqui está um exemplo para que você se aprofunde no cenário de ataque comum. Bill tem uma carteira inteligente que verifica se Bill é o iniciador de uma transferência. Uma vez, Bill cunhou um NFT num site de phishing. Isso permitiu que o site obtivesse a identidade de Bill e iniciasse uma transferência da sua carteira inteligente usando a sua identidade, resultando em perdas de ativos. Em circunstâncias normais, é menos provável que os utilizadores caiam nesta armadilha, mas ao interagir com dApps usando uma carteira, muitas vezes esquecem-se de verificar os avisos de interação. Por exemplo, se ambos envolverem a função Mint(), os utilizadores descuidados podem facilmente cair numa armadilha de phishing. A lógica de negócio dentro do site de phishing está repleta de armadilhas, por isso é importante verificar os avisos de interação para detetar erros durante as interações regulares.

Contrato de Carteira Inteligente

O contrato de carteira inteligente inclui uma função:

  • transfer(): Uma função de levantamento que só pode ser iniciada pelo proprietário da carteira, que neste caso é Bill.

Contrato de Ataque de Phishing

Num contrato de ataque de phishing, Mint() induz os utilizadores a transferir fundos para o endereço de um hacker. Inclui uma função:

  • Mint(): Uma vez chamada, a função de phishing executa internamente transfer() do contrato Wallet. Como o iniciador original é o próprio utilizador (neste exemplo, Bill), a verificação require(tx.origin == owner, "Not owner") ; não será um problema. No entanto, o endereço de destino para a transferência já foi adulterado para o endereço do hacker, resultando em roubo de fundos.

Soluções

1. Usar msg.sender em vez de tx.origin

Independentemente de quantas chamadas de contrato estejam envolvidas (Contrato A → Contrato B →…→ contrato alvo), apenas verifique msg.sender, ou seja, o chamador direto, para evitar ataques causados por contratos intermediários maliciosos.

2. Verificar tx.origin == msg.sender

Este método pode manter os contratos maliciosos afastados, mas os desenvolvedores precisam considerar as suas próprias realidades de negócio, pois efetivamente isola todas as outras chamadas de contrato externas.

3. Ataque de Gerador de Números Aleatórios (RNG)

Isto remonta à tendência das dApps de jogos de azar ou apostas por volta de 2018 e 2019. Tipicamente, os desenvolvedores usam certas sementes em contratos inteligentes para gerar números aleatórios para selecionar vencedores durante os sorteios. Sementes comuns incluem block.number, block.timestamp, blockhash e keccak256. No entanto, os mineradores podem controlar totalmente estas sementes, portanto, em alguns casos, mineradores maliciosos podem manipular as variáveis para colher benefícios.

Contratos de Dados Comuns

O contrato de Dados inclui uma função:

  • Bet(): Uma função de aposta onde os utilizadores introduzem um número de aposta e pagam um ETH. Um número aleatório é gerado com múltiplas sementes, e se o número da aposta corresponder ao número aleatório, o utilizador ganha todo o prémio.

Contrato de Ataque do Minerador

Os mineradores podem ganhar desde que pré-calculem o número aleatório vencedor e o executem no mesmo bloco. Isto inclui uma função:

  • attack(): Uma função de ataque de aposta, onde o minerador pré-calcula o número aleatório vencedor. Como é executado no mesmo bloco, blockhash(block.number - 1) e block.timestamp no mesmo bloco são iguais. Em seguida, o minerador chama Bet() do contrato de Dados para completar o ataque.

Solução

Usar números aleatórios off-chain fornecidos por projetos oracle

Através de serviços fornecidos por projetos oracle como o Chainlink, números aleatórios on-chain são injetados em contratos on-chain para garantir aleatoriedade e segurança. No entanto, os projetos oracle também carregam riscos de centralização, necessitando assim de serviços oracle mais maduros.

4. Ataque de Repetição

Um ataque de repetição envolve reiniciar uma transação usando uma assinatura previamente utilizada para roubar fundos. Um dos ataques de repetição mais conhecidos nos últimos anos foi o roubo de 20 milhões de tokens $OP do criador de mercado Wintermute na Optimism, que foi um ataque de repetição entre cadeias. Como a conta da carteira multisignatura da Wintermute foi temporariamente implantada apenas na rede principal Ethereum, o hacker usou a assinatura da transação para a implantação de um endereço multisignatura da Wintermute no Ethereum para reexecutar a mesma transação na cadeia Optimism, ganhando assim controle da conta da carteira multisignatura na Optimism. Uma conta de carteira multisignatura é essencialmente uma conta de contrato inteligente, o que também demonstra uma diferença significativa entre SCA e EOA. Para uma EOA, um usuário normal precisa apenas de uma chave privada para controlar todos os endereços no Ethereum e cadeias compatíveis com EVM (as strings de endereço são exatamente as mesmas), enquanto uma SCA é efetiva apenas em uma cadeia após ser implantada.

Lógica Específica

Aqui, fornecemos um exemplo de um ataque de repetição típico (ataque de repetição na mesma cadeia). Bill tem uma carteira inteligente que requer que ele insira sua assinatura eletrônica antes que cada transação possa ser executada. Agora que a hacker Lucy roubou a assinatura eletrônica de Bill, ela pode iniciar um número ilimitado de transações para drenar a carteira inteligente de Bill.

Exemplo

Um contrato com vulnerabilidades consiste em três funções:

  • checkSig(): Função de verificação ECDSA, garantindo que o resultado da verificação seja o signatário originalmente definido.
  • getMsgHash(): Função para gerar hash, que combina para e quantidade para formar o hash.
  • transfer(): Função de transferência, permitindo que os usuários retirem fundos do pool de liquidez. Devido à falta de restrições na assinatura, a mesma assinatura pode ser reutilizada, permitindo que hackers roubem fundos continuamente.

Solução

Inclua o nonce na combinação de assinatura para evitar ataques de repetição. O princípio do parâmetro é o seguinte:

  • nonce: Descreve a variável do número de transações de uma EOA na rede blockchain. Tem ordem e unicidade. A cada transação adicional, o valor do nonce aumentará em 1. A rede blockchain verificará se o nonce da transação é consistente com o nonce atual da conta. Portanto, um hacker falharia se usasse uma assinatura já utilizada, pois o valor do nonce na combinação de assinatura seria menor que o valor atual do nonce da EOA.

5. Ataque de Negação de Serviço (DoS)

O ataque de Negação de Serviço (DoS) não é novidade no mundo tradicional da Web2. Refere-se a qualquer interferência em um servidor, como o envio de uma grande quantidade de informações indesejadas ou disruptivas, prejudicando ou destruindo completamente a disponibilidade. Da mesma forma, os contratos inteligentes são afetados por tais ataques, que essencialmente visam fazer com que o contrato inteligente funcione mal.

Lógica Específica

Vejamos um exemplo. O Projeto A está realizando uma oferta pública para o token do protocolo, onde todos os usuários podem contribuir com fundos para o pool de liquidez (Contrato Inteligente) para comprar cotas por ordem de chegada, e os fundos excedentes serão devolvidos aos participantes. A hacker Alice explora o contrato de ataque para participar da oferta pública. Uma vez que o pool de liquidez tenta devolver os fundos para o contrato de ataque de Alice, um ataque DoS será acionado, impedindo que a ação de devolução seja realizada. Como resultado, uma grande quantidade de fundos fica bloqueada no contrato inteligente.

Exemplo de Contrato de Oferta Pública

Exemplo

O contrato de oferta pública inclui duas funções:

  • deposit(): função de depósito, registrando o endereço do depositante e o montante contribuído.
  • refund(): função de reembolso, com a qual a equipe do projeto devolve os fundos aos investidores.

Contrato de Ataque DoS

O contrato de ataque DoS inclui uma função:

  • attack(): Apesar de ser uma função de ataque, não possui nenhum problema. O principal problema reside na função de callback de pagamento receive() incorporada no contrato Hacker, que inclui um julgamento de exceções. Qualquer contrato externo que transfira fundos para o contrato Hacker desencadeará uma exceção através de revert(), impedindo assim a conclusão da operação.

Soluções

1. Evitar que funcionalidades críticas fiquem bloqueadas ao invocar contratos externos

Remover require(success, "Refund Fail!"); da função refund() acima do contrato PublicSale, garantindo que a operação de reembolso possa continuar mesmo se um reembolso para um único endereço falhar.

2. Desacoplamento

Na função refund() acima do contrato PublicSale, permitir que os usuários solicitem reembolsos por conta própria em vez de distribuir os reembolsos, minimizando assim interações desnecessárias com contratos externos.

6. Ataque permit

Num ataque de permissão, a Conta A fornece antecipadamente a assinatura para uma parte designada, e então a Conta B, ao obter a assinatura, pode realizar transferências de tokens autorizadas para roubar uma certa quantidade de tokens. Aqui, discutimos principalmente duas funções comuns para autorização de tokens em Contratos Inteligentes: approve() e permit().

No contrato ERC20 comum, a Conta A pode chamar approve() para autorizar uma certa quantidade de tokens para a Conta B, permitindo que esta última transfira esses tokens da primeira. Adicionalmente, permit() foi introduzido nos contratos ERC20 no EIP-2612, e a Uniswap lançou um novo padrão de autorização de tokens, Permit2, em novembro de 2022.

Lógica Específica

Aqui está um exemplo. Um dia, Bill estava navegando em um site de notícias blockchain quando de repente apareceu um pop-up de assinatura do Metamask. Como muitos sites ou aplicativos blockchain usam assinaturas para verificar logins de usuários, Bill não pensou muito nisso e completou a assinatura diretamente. Cinco minutos depois, seus ativos no Metamask foram drenados. Bill então descobriu no explorador da blockchain que um endereço desconhecido iniciou uma transação permit(), seguida por uma transação transferFrom() que esvaziou sua carteira.

Exemplo

As duas funções são as seguintes:

  • approve(): Uma função de autorização padrão onde a Conta A autoriza uma certa quantidade de fundos para a Conta B.
  • permit(): Uma função de autorização por assinatura onde a Conta B submete e completa a verificação da assinatura para obter a quantidade autorizada da Conta A. Os parâmetros incluem o proprietário concedendo autorização, o gastador sendo autorizado, a quantidade autorizada, o prazo da assinatura, e os dados de assinatura v, r, e s do proprietário.

Soluções

1. Preste atenção a cada assinatura nas interações on-chain

Apesar das medidas que algumas carteiras tomam para decodificar e exibir informações de assinatura de autorização approve(), elas não fornecem praticamente nenhum aviso para phishing de assinatura permit(), aumentando o risco de ataques. Portanto, é fortemente recomendado inspecionar rigorosamente cada assinatura desconhecida para garantir se ela é direcionada à função permit().

2. Separe a carteira para interação regular da carteira que armazena ativos

Isso é extremamente importante para os usuários de criptomoedas, especialmente caçadores de airdrops, pois eles interagem com inúmeros dApps ou sites todos os dias e são propensos a armadilhas. Armazenar apenas uma pequena quantidade de fundos em uma carteira para interação regular pode manter as perdas dentro de um intervalo gerenciável.

7. Ataque Honeypot

Na indústria blockchain, um ataque honeypot refere-se a um tipo de contratos de token maliciosos implantados por equipes de projetos. O contrato concede apenas à equipe do projeto a permissão para vender, enquanto os usuários regulares só podem comprar em vez de vender, sofrendo assim perdas.

Lógica Específica

Aqui está um exemplo. Em um anúncio no Telegram, o Projeto A informa aos usuários que o token foi implantado na rede principal e está disponível para negociação. Como o token só pode ser comprado e não pode ser vendido, o preço continua subindo inicialmente, e os usuários que temem perder a oportunidade continuam comprando. Após algum tempo, quando os usuários descobrem que não é possível vender, a equipe do projeto aproveita a oportunidade e despeja os tokens, fazendo o preço despencar.

Exemplo

Função principal:

  • _beforeTokenTransfer(): Uma função interna chamada durante as transferências de tokens, que só pode ter sucesso quando chamada pelo proprietário; chamadas de outras contas falharão.

Solução

Utilizar ferramentas de verificação de segurança

a. Token Sniffer para tokens Ethereum

b. Ave Check para tokens em outras cadeias

c. Websites de mercado com ferramentas de detecção incorporadas, como o Dextools

Evite negociar tokens com pontuações baixas.

8. Ataque de Front-Running

O front-running surgiu originalmente nos mercados financeiros tradicionais, onde a assimetria de informações permitia que intermediários financeiros obtivessem lucros através de ações rápidas baseadas em informações específicas do setor. Na indústria blockchain, o front-running decorre principalmente do front-running na cadeia, que envolve a manipulação de mineradores para priorizar o empacotamento das próprias transações na cadeia para obter lucros.

No campo blockchain, os mineradores podem lucrar manipulando as transações que empacotam nos blocos, por exemplo, excluindo certas transações e reordenando outras. Esse lucro pode ser medido com o Valor Extraível pelo Minerador (MEV). Antes que a transação de um usuário seja adicionada à rede principal Ethereum, a maioria das transações é agregada na mempool. Os mineradores procuram transações com preços de gás mais altos nesta mempool e priorizam o seu empacotamento para maximizar seus ganhos. Geralmente, transações com preços de gás mais altos são mais facilmente empacotadas pelos mineradores. Enquanto isso, alguns bots de MEV também vasculham a mempool em busca de transações com lucratividade.

Lógica Específica

Abaixo está um exemplo. Bill descobre um novo token popular com flutuações significativas de preço. Para garantir o sucesso das transações de tokens na Uniswap, Bill define um intervalo de slippage excepcionalmente amplo. Infelizmente, o bot MEV de Alice detecta esta transação na mempool e prontamente aumenta a taxa de gás, iniciando uma transação de compra antes de Bill e inserindo uma transação de venda após a de Bill dentro do mesmo bloco. Após a confirmação do bloco, isso causa perdas significativas de slippage para Bill, enquanto Alice lucra com uma operação de arbitragem de compra baixa e venda alta.

Exemplo

A função é a seguinte:

  • solve(): Uma função de adivinhação onde qualquer pessoa pode submeter uma resposta, e se a resposta submetida corresponder à resposta alvo, o remetente pode receber 10 ethers.
  1. Processo: Bill encontra a resposta correta.
  2. Alice monitoriza o mempool, aguardando que alguém submeta a resposta correta.
  3. Bill chama solve() para submeter a resposta e define o preço do gás em 100 Gwei.
  4. Alice vê a transação enviada por Bill e descobre a resposta. Ela define um preço de gás mais alto do que o de Bill, 200 Gwei, e chama solve().
  5. A transação de Alice é empacotada pelo minerador antes da de Bill.
  6. Alice ganha uma recompensa de 10 ethers.

Solução

As três principais funções são as seguintes:

  • commitSolution(): Uma função para submeter resultados, colocando a resposta submetida pelo utilizador solutionHash, o tempo de submissão commitTime, e o estado revealed na estrutura Commit.
  • getMySolution(): Uma função para obter resultados, permitindo aos utilizadores visualizar as suas respostas submetidas e informações relacionadas, incluindo a resposta submetida pelo utilizador solutionHash, o tempo de submissão commitTime, e o estado revealed.
  • revealSolution(): Uma função para reivindicar recompensas por adivinhar o puzzle, permitindo aos utilizadores reivindicar recompensas após fornecer a resposta e a senha que definiram.

Processo:

  1. Bill encontra a resposta correta.
  2. Bill chama commitSolution() para submeter a resposta correta.
  3. No próximo bloco, Bill chama revealSolution(), fornecendo a resposta e a senha que ele definiu para reivindicar a recompensa.

Em commitSolution(), Bill submete uma string criptografada, mantendo os dados em texto simples submetidos apenas para si mesmo. Nesta etapa, o tempo do bloco de submissão commitTime também é registrado. Em seguida, em revealSolution(), o tempo do bloco é verificado para evitar front-running dentro do mesmo bloco. Como chamar revealSolution() requer a submissão da resposta em texto simples, esta etapa visa impedir que outros ignorem commitSolution() e chamem diretamente revealSolution(). Após a verificação bem-sucedida, a recompensa será distribuída se a resposta for verificada como correta.

Conclusão

Os contratos inteligentes desempenham um papel crucial na tecnologia blockchain e oferecem inúmeras vantagens. Em primeiro lugar, eles permitem a execução descentralizada e automatizada, garantindo a segurança e confiabilidade das transações sem intermediários. Em segundo lugar, os contratos inteligentes reduzem etapas intermediárias e custos, aumentando a eficiência das transações.

Apesar de tantos benefícios, os contratos inteligentes também enfrentam o risco de ataques que podem causar perdas financeiras aos usuários. Como tal, alguns hábitos são essenciais para os usuários on-chain. Primeiramente, os usuários devem sempre escolher cuidadosamente os dApps para interação e revisar minuciosamente o código do contrato e as regras relacionadas. Além disso, devem atualizar regularmente e usar carteiras seguras e ferramentas de interação com contratos para mitigar o risco de ataques de hackers. Ademais, é aconselhável armazenar seus fundos em múltiplos endereços para minimizar potenciais perdas decorrentes de ataques a contratos.

Para os participantes do setor, garantir a segurança e a estabilidade dos contratos inteligentes é igualmente importante. A primeira prioridade deve ser reforçar a auditoria dos contratos inteligentes para identificar e retificar potenciais vulnerabilidades e riscos de segurança. Em segundo lugar, os participantes do setor devem manter-se informados sobre os mais recentes desenvolvimentos em blockchain relacionados com ataques a contratos e tomar medidas de segurança em conformidade. Por último, mas não menos importante, devem também melhorar a educação dos utilizadores e a consciencialização de segurança em termos de utilização correta dos contratos inteligentes.

Em conclusão, com esforços concertados tanto dos utilizadores como dos participantes do setor, os riscos de segurança colocados pelos contratos inteligentes podem ser significativamente mitigados. Os utilizadores devem sempre selecionar cuidadosamente os contratos e salvaguardar os ativos pessoais, enquanto os participantes do setor devem intensificar a auditoria de contratos, manter-se a par dos avanços tecnológicos e melhorar a educação dos utilizadores e a consciencialização de segurança. Juntos, impulsionaremos o desenvolvimento seguro e fiável dos contratos inteligentes.



Referências:


Solidity by Example:https://solidity-by-example.org/

Blockchain Know-how of SlowMist:https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzU4ODQ3NTM2OA==&action=getalbum&album_id=1378673890158936067&scene=173&from_msgid=2247498135&from_itemidx=1&count=3&nolastread=1#wechat_redirect

Chainlink - Top 10 DeFi Security Best Practices:https://blog.chain.link/defi-security-best-practices/#post-title

WTF - Solidity 104 Contract Security:https://www.wtf.academy/solidity-104/

Vulnerabilities in DeFi Smart Contracts in 4 Categories with 38 Scenarios:https://www.weiyangx.com/381670.html

OpenZeppelin:https://github.com/OpenZeppelin/

De acordo com os requisitos regulamentares dos departamentos relevantes para a indústria de cripto não podemos fornecer serviços aos utilizadores na região onde o seu IP está localizado.