WEB3DEV

Cover image for Vulnerabilidades Aritméticas em Contratos Inteligentes
Panegali
Panegali

Posted on

Vulnerabilidades Aritméticas em Contratos Inteligentes

Prefácio

Este é o quarto de uma série de artigos sobre vulnerabilidades às quais os contratos inteligentes são suscetíveis. Você pode encontrar o resto dos artigos nos seguintes links:

  1. Dependência da Ordem de Transação.
  2. Reentrância.
  3. Maleabilidade da Assinatura.
  4. Vulnerabilidades aritméticas [Este artigo]

O artigo anterior era pesado, então, para esta semana, queria manter as coisas leves. Como sempre, o propósito destes artigos não é educar os outros, mas testar a profundidade do meu próprio entendimento. Se eu posso explicá-lo corretamente, significa que eu o entendo corretamente. Espero que meus esforços sejam ao menos um pouco úteis para outra pessoa e os ajudem nesta jornada comigo.


Conteúdo

  1. Visão geral de overflows e underflows
  2. Risco de segurança
  3. Medidas de mitigação
  4. Referências

1. Visão geral de overflows e underflows

Overflows e underflows são uma vulnerabilidade clássica no mundo “normal” da infosec (segurança da informação). Estas falhas foram abusadas ao longo da história, levando a consequências graves - milhões de dispositivos infectados por spyware. De fato, a Common Weakness Enumeration (CWE) colocou os overflows e underflows como a 12ª falha mais comum em 2021.

Isso é legal e tudo, mas o que é overflows e underflows (CWE-190 e CWE-191, respectivamente)? Permita-me responder a essa pergunta com uma história sobre um jovem chamado Bob e seu carro mágico.

Bob ganhou um velho Maruti Suzuki 800 como seu primeiro carro em seu aniversário de 18 anos. Bob finge seu entusiasmo, mas ainda é grato a seus pais, mas é óbvio que o carro está por aí há mais tempo do que ele está vivo!

1

(não) o carro de Bob

O hodômetro indica que o carro percorreu um total de 99.997 quilômetros - o que parece impossível e um pouco interessante. Por curiosidade, Bob decide encontrar Alice na casa dela, que fica a 3 quilômetros de distância. Durante a condução, o hodômetro indica 99998 e, finalmente, 99999. Após o quilômetro final, o hodômetro marca 00000.

O carro já percorreu um total de 0 km, tornando-o novo! Uma fada saltou do porta-luvas e lançou um feitiço. A maçaneta da porta não está mais enferrujada, as rachaduras no para-brisa foram consertadas, a mancha suspeita no banco do passageiro desapareceu e o cinto de segurança não cheira mais como o perímetro de um banheiro Chipotle. O carro até mudou de cor de um verde deprimente para um vermelho vívido! Bem, na verdade não, o hodômetro simplesmente transbordou (overflowed). Desculpe Bob…

Isso aconteceu porque o hodômetro não consegue exibir mais de 5 dígitos. Portanto, quando o primeiro número de 6 dígitos aparecer (100000), ele pode exibir apenas os últimos 5. E o oposto é chamado de underflow inteiro.

Você pode pensar em overflow e underflow como Pacman aparecendo no lado direito da tela depois de passar pelo lado esquerdo e vice-versa.

2

1.1 Falhas aritméticas no Solidity

Vamos dar uma olhada mais de perto no contexto da ciência da computação para entender exatamente como essas vulnerabilidades ocorrem.

Cada ação que fazemos em nossos computadores; assistindo memes de gatos no YouTube, jogando uma quantidade doentia de Valorant, lendo um artigo no Medium de um autor que se acha engraçado - tudo se resume a zeros e uns sendo manipulados de maneiras matemáticas muito precisas. O mesmo vale para a EVM. Esses zeros e uns são chamados de strings de bytes.

Falhas aritméticas geralmente surgem quando strings de bytes excedem seu espaço alocado (tamanho do byte). Isso pode fazer com que o programa aja de maneiras inesperadas, que os hackers podem usar a seu favor.

Exatamente quanto espaço é alocado depende do tipo de dados usado. No caso do Solidity;

uint8 ranges from 0 to 2 - 1, 
uint16 ranges from 0 to 2¹⁶ - 1
...
uint256 ranges from 0 to 2²⁵⁶ - 1..
Enter fullscreen mode Exit fullscreen mode

Como alternativa, podemos usar o seguinte para obter o valor mínimo e máximo de um tipo (type) inteiro (x).

type(x).min
type(x).max
Enter fullscreen mode Exit fullscreen mode

2. Risco de segurança

O compilador do Solidity anterior às versões 0.8.0 não verifica overflows e underflows. Isso significa que se o tamanho do byte for atingido para um determinado tipo inteiro, ele assumirá o primeiro valor viável. Isso pode causar problemas menores, desde interromper a execução do contrato inteligente até problemas sérios que podem levar ao ganho de uma quantidade absurda de tokens ERC-20.

2.1 Prova de conceito

Vamos dar uma olhada no trecho de código a seguir.

// SPDX-License-Identifier: MIT
pragma solidity 0.7.0;

contract arithmetic {

    uint8 public underflow = 0;
    uint8 public overflow = 255;

    function _underflow() external {
        underflow--;
    }
    function _overflow() external {
        overflow++; 
    }
}
Enter fullscreen mode Exit fullscreen mode

Aqui, o contrato é muito simples. As variáveis ​​underflow e overflow são do tipo uint8 e possuem os valores 0 e 255, respectivamente. As funções _underflow() e _overflow() subtrairão 1 de underflow e _overflow adicionará 1 às variáveis ​​de overflow.

Estarei usando o framework brownie (porque estou mais familiarizado com Python).

3

Compilando o contrato usando brownie

4

Implantando o contrato localmente
$ brownie compile
$ brownie console
>>> acct = accounts[0]
>>> arith = arithmetic.deploy({'from': acct}
Enter fullscreen mode Exit fullscreen mode

Agora, temos acesso à ABI do contrato implantado. Podemos acessar as variáveis ​​públicas por meio de suas funções getter.

5

Funções getters de variáveis ​​públicas

Vamos chamar as funções _overflow() e _underflow() e verificar se nosso contrato inteligente está vulnerável.

6

Chamando as funções

Chamando as funções getter mais uma vez para verificar seus estados.

7

Overflow and underflow ocorreram

Aqui, podemos ver que o valor de overflow (255) foi alterado para 0, em vez de 256, porque excedeu o tamanho do byte de uint8.

O valor de underflow (0) foi alterado para 255 em vez de -1 porque a unidade não pode ficar abaixo de 0 e, em vez disso, o padrão é o valor máximo de uint8.

3. Medidas de mitigação

A correção mais fácil e rápida seria simplesmente usar versões do compilador Solidity mais recentes ou iguais a 0.8.0. Nessas versões, o contrato lançará um erro se tais vulnerabilidades ocorrerem.

8

Outro método para evitar tais vulnerabilidades seria usar a biblioteca safemath.sol do OpenZeppelin.

4. Referências


Artigo escrito por Aayushman Thapa Magar e traduzido por Marcelo Panegali

Top comments (0)