Este artigo foi originalmente publicado em https://www.buildbear.io/resources/guides-and-tutorials/Downcasting_Vulnerability
Nas versões 0.8 e superiores do Solidity, a verificação de overflow e underflow é habilitada por padrão. No entanto, essa verificação não se aplica a conversões entre tipos inteiros, incluindo conversões de tipo de dados.
Como resultado, as conversões podem levar ao overflow sem acionar uma reversão.
Permita-nos demonstrar este problema:
Para ajudá-lo a começar imediatamente, já preparamos um repositório que mostra essa vulnerabilidade. Vamos começar fazendo uma cópia do repositório.
Etapa 1: Clonar o repositório do tutorial
1.1 Bifurcando o repositório do tutorial:
- Visite nosso Repositório do Tutorial e clique no botão “Fork” para duplicar o repositório em sua própria conta. Aguarde até que o processo de bifurcação seja concluído antes de prosseguir.
1.2 Clonando o repositório localmente:
- Acesse seu repositório bifurcado no GitHub.
- Clique no botão “Code” (Código) e copie o URL fornecido (seja HTTPS ou SSH).
- Abra seu terminal ou Git Bash.
- Navegue até o diretório onde deseja colocar o repositório clonado.
- Use o seguinte comando para clonar o repositório:
git clone <cole o URL copiado aqui>
- Aguarde o processo de clonagem terminar. Depois de concluído, você deverá ver um novo diretório com o nome do repositório no diretório escolhido.
- Para acessar o repositório clonado, use o comando
cd <Nome do repositório clonado>
e navegue até a pasta DownCastingError executandocd DownCastingError
.
Etapa 2: Vamos examinar os contratos.
2.1 Contrato unsafeDownCasting.sol
Aqui está um detalhamento do código:
- O SPDX-License-Identifier especifica a licença sob a qual o contrato é lançado. Neste caso, é a licença MIT.
- A declaração
pragma solidity ^0.8.15;
define a versão do compilador Solidity que deve ser usada. Este contrato requer Solidity versão 0.8.15 ou superior. - O contrato é nomeado
unsafeDownCasting
. Possui uma variável de estado chamadaLuckyNumber
do tipouint
, que armazenará o número da sorte. - A função
setLuckyNumber
é uma função pública que recebe um parâmetrouint256
chamadoamount
. É usada para definir o número da sorte. Dentro dessa função, o valoramount
é convertido para umuint8
usando a sintaxeuint8
(amount
). Essa operação de downcasting (conversão de uma referência de uma superclasse (classe pai) para uma de suas subclasses (classes filhas)) pode levar a resultados inesperados se o valoramount
for maior que o valor máximo deuint8
, que é 255. Se oamount
exceder esse valor, apenas os 8 bits menos significativos serão armazenados na variávelnumber
, descartando os bits excedentes. Isso pode resultar em perda de dados e comportamento incorreto. - O valor
number
convertido é então atribuído à variávelLuckyNumber
de estado. - A função
getLuckyNumber
é uma função de exibição pública que retorna o valor atual da variávelLuckyNumber
. Permite que outros contratos ou entidades externas recuperem o número da sorte sem modificar o estado do contrato.
2.2 Contrato safeDownCasting.sol
- O contrato importa a biblioteca
SafeCast
dos contratos do OpenZeppelin. Essa biblioteca fornece funcionalidade segura de downcasting. - A instrução
using SafeCast for uint256
; permite que o contrato use a bibliotecaSafeCast
para o tipouint256
. - A função
setLuckyNumber
é uma função pública que recebe um parâmetrouint256
chamado_amount
. É usada para definir o número da sorte. Dentro dessa função, o valor_amount
é convertido com segurança para uint8 usando a funçãotoUint8()
da bibliotecaSafeCast
. Se o valor_amount
for maior que o valor máximo deuint8
, o downcast será revertido, garantindo que apenas downcasts válidos sejam executados. O valoruint8
resultante é então atribuído à variávelLuckyNumber
de estado.
Vamos implantar esses contratos e ver a vulnerabilidade em ação.
Etapa 3: Configurando a infraestrutura para implantar o contrato inteligente
3.1. Visite o aplicativo BuildBear. (Bem, podemos implantar este contrato no nó Hardhat local, mas usaremos o BuildBear, você poderá entender o motivo no final deste tutorial).
3.2. Crie sua Rede de Testes Privada. Você tem a opção de bifurcar das redes principais ou criar uma nova rede de testes do zero, usando a rede principal como base. A bifurcação da rede principal da Ethereum nos permite utilizar convenientemente NFTs e tokens existentes.
3.3. Adicione sua rede de testes privada à sua carteira MetaMask usando o botão “Add to Metamask” (Adicionar a Metamask):
Etapa 4: Implantando o Contrato Inteligente
Para começar, execute o seguinte comando para instalar os pacotes necessários:
npm install
4.1 Atualize o arquivo hardhat.config.js:
- Acesse seu Dashboard (painel) e clique em “verify contract” (verificar contrato).
- Copie os objetos BuildBear e Etherscan e atualize o arquivo
hardhat.config.js
com os novos valores.
4.2 Para implantar os contratos inteligentes safeDownCasting.sol
e unsafeDownCasting.sol
, execute o seguinte comando: npx hardhat deploy
. Isso executará os scripts de implantação localizados na pasta deploy
e salvará os detalhes da implantação, incluindo a ABI e o endereço do contrato, na pasta deployments
.
4.3 Testando a Vulnerabilidade
Como estamos usando o BuildBear, podemos interagir convenientemente com os contratos diretamente do explorer, eliminando a necessidade de escrever um script.
Clique em “Open Faucet” (abrir torneira) no painel e conecte sua carteira para cunhar tokens nativos. Com o BuildBear, temos nossa própria torneira, então não precisamos solicitar tokens como fazemos quando usamos redes de testes públicas.
Clique no link fornecido no terminal do contrato unsafeDownCasting
. Você será direcionado para a página Contract. Vá para a seção "Write Contract" (Escrever contrato).
Clique em “Connect to Web3”(Conectar à Web3). Agora o explorador está conectado à sua carteira. Digite qualquer número maior que 255 e clique em “Write”(Escrever) para assinar a transação na MetaMask.
Agora vá para a seção "Read Contract" (ler contrato). Você notará que o número da sorte é 44. Como o valor máximo que pode ser representado por um uint8
é 255, a operação de downcast causa um overflow. No Solidity, quando ocorre um overflow durante uma operação aritmética, o valor "dá a volta" e começa a partir do valor mínimo do tipo de dados. Neste caso, os 8 bits menos significativos de 300 (que é 44 em binário) serão armazenados na variável number
.
Como resultado, a variável LuckyNumber
de estado receberá o valor de 44.
4.4 Testando o Contrato com a Biblioteca SafeCast.
Agora vá para a página do explorador do contrato safeDownCasting a partir do link comprovado no Terminal, conecte a carteira e digite 300 e clique em write, como você pode ver, a transação falhou. Clique em "View Transaction" (Exibir transação). Como você pode ver, a transação falhou pelo motivo SafeCast: value doesn't fit in 8 bits
, porque a função SafeCast está verificando se o inteiro é menor que uint8 antes de convertê-lo em uint8.
Mitigamos com sucesso a vulnerabilidade de DownCasting usando a biblioteca SafeCast dos contratos do OpenZeppelin.
Se você apoia nossos esforços, siga-nos no Twitter e no LinkedIn. Se ainda não o fez, convidamos você a entrar em nosso grupo do Telegram clicando aqui.
Repositório do Github: Tutoriais do Buildbear
Sobre o BuildBear:
O BuildBear oferece aos desenvolvedores uma plataforma conveniente para criar uma rede de testes privada personalizada para testar seus DApps. Com a capacidade de bifurcar cadeias EVM, os desenvolvedores podem criar uma rede privada adaptada às suas necessidades.
Use o BuildBear.io e crie sua rede de testes privada agora!
Artigo escrito por BuildBear Team. Traduzido por Marcelo Panegali
Top comments (0)