Bem-vindo ao guia para iniciantes em auditoria de contrato inteligente! Uma das melhores maneiras de começar com a auditoria de contratos inteligentes é entrar e observar alguns tipos comuns de vulnerabilidades em contratos inteligentes.
Seria útil se você já tivesse um entendimento básico da linguagem de programação Solidity da Ethereum. Como veremos alguns dos códigos escritos por programadores de solidez Noob.
Ataque de Reentrada
Cenário do mundo real:
Imagine que você tem 50 chocolates. Você tem uma irmãzinha travessa a quem você permitiu levar apenas 2 chocolates de você a qualquer momento. Você também não quer dar mais de 10 chocolates para ela em um dia, temendo que ela tenha cáries. Para garantir isso, todas as noites você conta quantos chocolates ainda restam com você. Você acha que isso funcionaria? Ou você seria hackeado por sua irmãzinha?
Infelizmente, não vai funcionar! Sua irmã descobre que você não sabe quantos chocolates tem até a noite. Então, no dia seguinte, sua irmãzinha visita você 6 vezes antes da noite e leva 2 chocolates de cada vez! Isso é o que chamamos de ataque de reentrada.
Aqui você está atualizando a contagem de chocolates que você tem à noite em vez de atualizar a contagem toda vez que sua irmã pegar 2 chocolates de você. Isso também é o que acontece com o contrato inteligente. O contrato inteligente assume um saldo específico enquanto o invasor está realmente ocupado retirando uma certa quantidade de criptografia do contrato várias vezes.
Exemplo de código do mundo real:
Este código pertence a um contrato inteligente chamado Sem banco. Qualquer um pode retirar ether de um contrato Unbanked desde que os saldos do msg.sender (ou seja, o chamador do withdraw
função ) é maior ou igual ao valor solicitado para sacar.
function withdraw(uint _amount) { require(balances[msg.sender] >= _amount); msg.sender.call.value(_amount)(); balances[msg.sender] -= _amount;
}
Observe que há uma palavra-chave chamada usada para enviar a quantidade necessária de éter para o msg.sender
. Um invasor pode explorar isso criando um contrato chamado Thief no qual ele chama a função de retirada em um fallback()
função. UMA fallback()
function no Solidity é uma função especial que é executada quando o ether é enviado para o contrato inteligente.
Isso significa que um invasor pode chamar recursivamente a função de retirada. Assim, antes das atualizações do contrato inteligente, os saldos de msg.sender
na última linha de código, o invasor já retirou o éter várias vezes. Isso poderia ser evitado se os saldos fossem atualizados antes de usar a palavra-chave call, seguindo assim uma verifica-efeito-interações padrão.
Impacto:
A primeiro ataque de reentrada aconteceu em 2016 em uma DAO (Organização Autônoma Descentralizada) que resultou em aproximadamente US $ 50 milhões em hacks. Para reverter esse hack, a comunidade Ethereum dividiu a blockchain Ethereum que deu origem ao ETC (Ethereum Classic) e ETH (Ethereum).
Estouro e estouro aritmético
Cenário do mundo real:
Vamos jogar um jogo de pensamento. Consiste em um Gire a roleta, e o vencedor é decidido com base no maior número que conseguir ao girar a roleta. A roda está marcada de 256 a -256.
As regras do jogo são que o ponteiro para todos os jogadores repousa em 0 no início de cada rodada. E um jogador pode girar apenas na direção dos números negativos. Como você vai ganhar este jogo?
Uma boa estratégia para ganhar este jogo todas as vezes seria girar a roda com tanta força que a roda gira até -256 e depois gira para 256 de uma só vez. Isso é possível porque 256 vem logo após -256 na roda. Isso é o que chamamos de underflow aritmético. E estouro aritmético é apenas vice-versa disso.
Exemplo de código do mundo real:
An transbordar ou transbordar acontece quando uma operação aritmética atinge seu mínimo ou máximo.
function withdraw(uint _amount) public { require(balances[msg.sender] - _amount > 0); address payable to = payable(msg.sender); to.transfer(_amount); balances[msg.sender] -= _amount;
}
A _amount
parâmetro da função de retirada é um inteiro sem sinal. O valor do mapeamento de saldos (que é como um dicionário em python ou um par chave-valor em C++ ou Java) também é um inteiro sem sinal.
mapping(address => uint256) public balances
A declaração exigida verifica se os saldos de msg.sender
é positivo ou não. Mas esta afirmação será sempre verdadeira mesmo que o valor seja maior que os saldos de msg.sender
. Isso porque tanto o balances
e _amount
variáveis são do tipo unsigned integer e seu resultado aritmético (após underflow) também será um unsigned integer!
E como você deve se lembrar, um inteiro sem sinal é sempre positivo. Isso significa que um invasor pode retirar uma quantidade ilimitada de Ether do contrato inteligente! Você pode encontrar um exemplo detalhado e um código de implementação para esta vulnerabilidade SUA PARTICIPAÇÃO FAZ A DIFERENÇA.
Outra coisa crucial a ser observada aqui é que a operação aritmética entre, digamos, dois inteiros sem sinal também é um inteiro sem sinal. Pode ser perigoso se isso for negligenciado em contratos inteligentes, pois pode resultar em violações de segurança indesejadas!
function votes(uint postId, uint upvote, uint downvotes) { if (upvote - downvote < 0) { deletePost(postId) }
}
Como você deve ter notado no exemplo acima, a instrução if é bastante inútil, pois upvote - downvote
sempre será positivo. E a postagem será excluída mesmo se downvotes
é melhor que upvotes
. Para evitar tais ataques, é recomendável usar uma versão do compilador Solidity maior que 0.8.0.
Impacto:
Uma moeda chamada moeda PoWH foi lançado em 2017. Embora fosse um jogo Ponzi, ele próprio foi hackeado devido a um bug de estouro aritmético que resultou em uma perda de cerca de 866 ETH ou US$ 950,000 na época. Você pode ler sobre isso em detalhes SUA PARTICIPAÇÃO FAZ A DIFERENÇA.
Deve ler: Lições do ataque a Tinyman, maior DEX em Algorand
Ataque de negação de serviço
Cenário do mundo real:
Imagine que você está em uma Bitcoin Tech University. Tudo parece bem, exceto que há uma mesa de jantar comum para todos. E, infelizmente, são poucas as pessoas de outra turma que sempre conseguem ocupar a mesa de jantar antes de alguém da sua turma.
No cenário prático, estão negando o serviço essencial a todos, resultando em perda de tempo precioso. Isso é o que chamamos de 'ataque de negação de serviço'.
Exemplo de código do mundo real:
No jogo chamado Rei do Éter, qualquer um pode se tornar um rei. Mas a regra para se tornar rei é que uma pessoa deve depositar mais éter do que o rei atual. Isso pode ser feito chamando o claimThrone()
função do contrato do Rei do Éter no qual a pessoa envia o éter diretamente ao rei anterior e se torna o novo rei.
function claimThrone() external payable { require(msg.value > balance, "Need to pay more to become the king"); (bool sent, ) = king.call{value: balance}(""); require(sent, "Failed to send Ether"); balance = msg.value; king = msg.sender; }
Como você deve ter adivinhado, esse código é vulnerável a um ataque DoS, mas como? Para isso, você precisa entender que existem dois tipos de endereços no Ethereum - o primeiro é o endereço de uma empresa externa conta própria ou simplesmente o endereço de uma carteira, e o segundo é o endereço do contrato. Agora, o éter pode ser enviado de qualquer um desses tipos de endereço.
Se isso, o éter for enviado pelo endereço do contrato, o contrato se tornará o rei. Mas vamos supor que este novo contrato não tenha um fallback()
função que é necessária se o contrato quiser aceitar éter. Então, se uma nova pessoa aparecer e tentar ligar para o claimThrone()
função, ele sempre falhará!
Observe que isso também acontece em parte porque o claimThrone()
A função verifica explicitamente se a transferência de ether foi bem-sucedida ou não na segunda instrução necessária. Você pode encontrar o código completo e fazer um ataque DoS nele SUA PARTICIPAÇÃO FAZ A DIFERENÇA.
Também é possível que um código seja vulnerável a um ataque DoS se o código tiver um loop em uma matriz de tamanhos grandes. Isso acontece porque o limite de gás pode ser excedido nesses casos. Você pode ler sobre isso SUA PARTICIPAÇÃO FAZ A DIFERENÇA.
Impacto:
Um jogo chamado Governamental, que aparentemente era um esquema Ponzi, ficou preso com 1100 éter porque era necessária uma grande quantidade de gás para processar o pagamento.
Aleatoriedade insegura
Cenário do mundo real:
Era uma vez um homem chamado Hesky que estava sempre acompanhado de seu macaco Pesky. Hesky realizou jogos de loteria e obteve bons lucros. Um dia Alice notou Hesky olhando fixamente para seu macaco Pesky. Então ela o viu escrevendo algo em um pedaço de papel e o selou em um envelope. Curiosa, ela decidiu investigar mais.
Mais tarde naquela noite, Alice viu que o vencedor da loteria foi decidido abrindo publicamente o envelope lacrado. Depois de observá-lo por alguns dias, Alice descobriu que o Hesky decidiu o número vencedor da loteria olhando para os gestos de Pesky (por exemplo, se o macaco coçou a cabeça, Hesky escreveu 10)! Agora Alice tinha a fórmula para ganhar cada loteria e só tinha que comprar o bilhete de loteria com o número certo!
Hesky havia assumido que sua maneira “aleatória” de decidir o vencedor da loteria nunca poderia ser descoberta, mas ele estava de fato incorreto.
Exemplo de código do mundo real:
Neste exemplo, um número aleatório é gerado com base no hash da combinação do número de um bloco e seu carimbo de data/hora do bloco. esse hash é então atribuído à variável de resposta. Agora, quem adivinhar esse número (aparentemente) aleatório é recompensado com 1 Ether. Você acha que isso é inacessível?
function guess(uint _guess) public { uint answer = uint( keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)) ); if (_guess == answer) { (bool sent, ) = msg.sender.call{value: 1 ether}(""); require(sent, "Failed to send Ether"); } }
Não! Um invasor ainda pode adivinhar esse número aleatório simplesmente copiando e colando o código para gerar o valor atribuído à variável de resposta e passar a mesma variável de resposta para o guess()
função!
guessTheRandomNumber.guess(answer);
Você pode encontrar o código completo SUA PARTICIPAÇÃO FAZ A DIFERENÇA. Para evitar este ataque, é recomendado usar uma função aleatória verificável, como o VRF Chainlink.
Impacto:
Cerca de 400 ETH foram perdidos devido a um ataque ao loteria Smart Billions contrato. Surpreendentemente, até a própria loteria do contrato era um esquema Ponzi (ai!).
Manipulação do tempo
Cenário do mundo real:
Satoshi adora comer biscoitos. Ele adora todos os tipos de biscoitos que sua mãe faz. Mas sua mãe é muito rigorosa e acha que comer muitos biscoitos não é bom para ele. Então, sua mãe estabelece uma regra de que ele só receberá os biscoitos às 8h.
Naquele mesmo dia, às 7h45, Satoshi corre para a mãe e pede biscoitos. Sua mãe pergunta: “Que horas são?”
"São 8 horas!" - ele responde.
"OK. Então pegue os biscoitos do meu armário.”
E assim, Satoshi foi capaz de manipular o tempo com sucesso em 15 minutos para que ele pudesse pegar seus biscoitos! Que cara com fome de biscoito!
Exemplo de código do mundo real:
O timestamp de um bloco pode ser manipulado por cerca de 15 segundos por um mineiro. Dessa forma, um minerador pode definir um timestamp favorável e incluir sua transação no mesmo bloco que ele minera. A função play()
pertence a um contrato de jogo chamado G-Dot.
function play() public { require(now > 1640392200 && neverPlayed == true); neverPlayed = false; msg.sender.transfer(1500 ether);
}
Este contrato recompensa 1500 ether para o jogador que for o primeiro a chamar a função play. Mas como você pode ver, a função play só pode ser chamada se o now ou block.timestamp da transação que contém a chamada para o play()
função é maior que a época 1640392200.
Um minerador pode facilmente manipular este timestamp e incluir sua transação de chamar o play()
função no mesmo bloco de modo que ele próprio seja o primeiro jogador. Dessa forma, é garantido que o mineiro vencerá o jogo!
Impacto:
O block.timestamp foi usado para gerar números aleatórios no Governamental e, portanto, era vulnerável a ataques de manipulação de tempo.
Entre em contato com QuillAudits
QuillAudits é uma plataforma de auditoria de contrato inteligente e segura projetada por QuillHash
Tecnologias.
É uma plataforma de auditoria que analisa e verifica rigorosamente contratos inteligentes para verificar vulnerabilidades de segurança por meio de revisão manual eficaz com ferramentas de análise estática e dinâmica, analisadores de gás e assimuladores. Além disso, o processo de auditoria também inclui testes de unidade extensivos, bem como análise estrutural.
Realizamos auditorias de contrato inteligentes e testes de penetração para encontrar potencial
vulnerabilidades de segurança que podem prejudicar a integridade da plataforma.
Se você precisar de assistência na auditoria de contratos inteligentes, sinta-se à vontade para entrar em contato com nossos especialistas aqui!
Para estar em dia com nosso trabalho, Junte-se à nossa comunidade: -
Twitter | LinkedIn | Facebook | Telegram
O posto Guia para iniciantes em auditoria de contrato inteligente: parte 1 apareceu pela primeira vez em Blog Quillhash.
Fonte: https://blog.quillhash.com/2022/01/19/beginners-guide-to-smart-contract-auditing-part-1/
- "
- &
- 000
- 2016
- 7
- Sobre
- Conta
- endereço
- Todos os Produtos
- já
- Apesar
- análise
- auditor
- Autônomo
- Começo
- MELHOR
- Bitcoin
- blockchain
- Bug
- comprar
- chamada
- casos
- Cheques
- clássico
- código
- Moeda
- combinação
- comum
- comunidade
- contém
- contract
- contratos
- bolinhos
- poderia
- Criar
- cripto
- Atual
- DAO
- dia
- Descentralizada
- Denial of Service
- detalhe
- Dex
- down
- facilmente
- comer
- ETH
- Ether
- ethereum
- Blockchain Ethereum
- Ethereum Classic
- exemplo
- Explorar
- final
- Primeiro nome
- Gratuito
- função
- jogo
- Games
- GAS
- gerar
- GitHub
- vai
- Bom estado, com sinais de uso
- guia
- cortar
- hacks
- hash
- cabeça
- SUA PARTICIPAÇÃO FAZ A DIFERENÇA
- Como funciona o dobrador de carta de canal
- HTTPS
- investigar
- IT
- Java
- juntar
- saltar
- Rei
- língua
- grande
- Line
- longo
- procurando
- loteria
- homem
- milhão
- mãe
- números
- organização
- Papel
- padrão
- Pagar
- Pessoas
- peça
- plataforma
- Jogar
- jogador
- ponzi
- Esquema Ponzi
- poder
- processo
- Programadores
- Programação
- público
- reverso
- rever
- Recompensas
- regras
- Satoshi
- segurança
- conjunto
- smart
- smart contract
- Smart Contracts
- So
- solidez
- algo
- Spin
- divisão
- começado
- Declaração
- Estratégia
- bem sucedido
- entraram com sucesso
- tecnologia
- testes
- Através da
- tempo
- ferramentas
- transação
- sem acesso
- universidade
- Atualizações
- valor
- vulnerabilidades
- vulnerabilidade
- Vulnerável
- Wallet
- O Quê
- Roda
- QUEM
- ganhar
- Atividades:
- escrita