Back to Blog

Como um Bug Crítico na Rede Solana Foi Detectado e Corrigido a Tempo

Code Auditing
June 7, 2022
6 min read

Em abril, nosso sistema de detecção de vulnerabilidades descobriu um problema no rBPF da Solana (ou seja, a máquina virtual onde todos os dApps da Solana são executados: https://github.com/solana-labs/rbpf). Após uma investigação cuidadosa, descobrimos que se trata de uma vulnerabilidade de segurança que pode levar a um caminho de execução incorreto de um contrato. Relatamos o bug à equipe de segurança da Solana, e a equipe imediatamente confirmou e corrigiu a vulnerabilidade. Além disso, eles concederam à nossa equipe uma recompensa no valor de $800.000 USD em tokens SOL.

A vulnerabilidade existe em versões mais recentes do rBPF (0.2.26 a 0.2.27). Quando relatamos o problema, os validadores usados na mainnet ainda não haviam sido atualizados para a(s) versão(ões) afetada(s). Nosso sistema detectou o problema antes que a versão afetada fosse incorporada, tornando o validador da mainnet imune a essa vulnerabilidade.

Detalhamos o problema desta vulnerabilidade a seguir.

1. eBPF e rBPF

eBPF (Extended Berkeley Packet Filter) foi inicialmente desenvolvido para filtrar pacotes no kernel. Devido à segurança, eficiência e escalabilidade do eBPF, ele é agora utilizado em diversas áreas como redes, rastreamento, perfilamento, etc. Considerando a rica capacidade do eBPF, a Solana o utiliza como mecanismo de execução para contratos inteligentes. Para construir dApps na Solana, os desenvolvedores desenvolvem seus contratos inteligentes em Rust e compilam o contrato em bytecode eBPF.

A Solana usa o rBPF, uma máquina virtual escrita em Rust, para executar o bytecode BPF compilado. No entanto, se a máquina virtual proposta (ou seja, rBPF) é robusta, segura e precisa, ainda é uma questão em aberto. Se existirem problemas de segurança dentro do rBPF, todos os validadores que contêm o rBPF podem ser afetados, resultando em grandes perdas (por exemplo, Perda de Fundos) para toda a rede Solana.

2. Causa Raiz

Desenvolvemos uma ferramenta que pode localizar automaticamente bugs de implementação do rBPF e escanear o código do rBPF periodicamente. Um problema grave foi identificado no rBPF (versão 0.2.26) durante o processo de escaneamento, que pode levar ao caminho de execução incorreto de um contrato.

Especificamente, a instrução sdiv é usada como instrução de divisão com sinal, introduzida como uma funcionalidade habilitada por padrão no rbpf 0.2.26. sdiv suporta divisão para operandos em 32 bits (ou seja, sdiv32) e 64 bits (sdiv64). Para a instrução sdiv32, o resultado do cálculo é armazenado no registrador bpf, que é de 64 bits. No entanto, se as instruções após a instrução sdiv32 lerem o resultado do cálculo como 64 bits, o resultado pode ser diferente. Isso ocorre porque o rBPF não estende o resultado do cálculo do sdiv32 para o valor correto em 64 bits durante a compilação JIT.

Por exemplo, se um número positivo (ou seja, 12) é dividido por um número negativo (ou seja, -4) com sdiv32, o resultado correto deveria ser -3 tanto em 32 bits quanto em 64 bits. O código abaixo é um exemplo.

Após executar e rastrear nos modos JIT e Interpretado, pudemos observar as diferenças entre eles:

2.1 Modo Interpretado

0 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    29: lddw r5, 0x10000000c
 1 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 000000010000000C, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    31: sdiv32 r5, -4
 2 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, FFFFFFFFFFFFFFFD, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    32: jslt r5, 0, lbb_7
 3 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, FFFFFFFFFFFFFFFD, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    36: exit

2.2 Modo JIT

0 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    29: lddw r5, 0x10000000c
 1 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 000000010000000C, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    31: sdiv32 r5, -4
 2 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 00000000FFFFFFFD, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    32: jslt r5, 0, lbb_7
 3 [0000000000000000, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 00000000FFFFFFFD, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    33: lddw r0, 0x1
 4 [0000000000000001, 0000000400000000, 0000000000000000, 0000000000000000, 0000000000000000, 00000000FFFFFFFD, 0000000000000000, 0000000000000000, 0000000000000000, 0000000000000000, 0000000200014000]    35: exit

No modo Interpretado, o registrador r5 é definido como 0xFFFFFFFFFFFFFFFD (-3 tanto no modo de 32 bits quanto no de 64 bits), enquanto no modo JIT, r5 é definido como 0x00000000FFFFFFFD. Neste caso, r5 será reconhecido como um número positivo (ou seja, 0x00000000FFFFFFFD) para a instrução jslt, que recebe um valor de 64 bits. Após isso, o caminho de execução estará completamente errado.

3. Impacto

Essa implementação incorreta pode levar ao caminho de execução incorreto de um contrato e pode causar problemas sérios.

Por exemplo, se uma operação essencial em um contrato inteligente depende do resultado da instrução sdiv32, isso pode levar a resultados de execução incorretos e ser explorado por atacantes.

Este problema foi introduzido em https://github.com/solana-labs/rbpf/pull/283, o que significa que o rBPF é vulnerável a partir da versão 0.2.26. Identificamos o problema e o relatamos à equipe de segurança da Solana em 28 de abril de 2022. A equipe respondeu rapidamente ao nosso relatório e corrigiu o problema em poucas horas adicionando a operação de extensão de sinal para a instrução sdiv32. A correção está em https://github.com/solana-labs/rbpf/pull/310. Devido à detecção e ao relatório oportuno da nossa equipe, os validadores da mainnet não foram afetados por esta vulnerabilidade.

Este problema é classificado como um bug de vivacidade de protocolo, resultando em uma recompensa de $800.000 concedida pela Solana.

Linha do Tempo

  • 2022/04/28: Relatamos o problema à equipe de segurança da Solana
  • 2022/04/29: A vulnerabilidade foi corrigida
  • 2022/05/09: CVE-2022-23066 foi atribuído
  • 2022/06/01: A recompensa por bug foi concedida

Sobre a BlockSec

A Equipe BlockSec se concentra na segurança do ecossistema blockchain e colabora com os principais projetos DeFi para proteger seus produtos. A equipe é fundada por pesquisadores de segurança de alto nível e especialistas experientes tanto da academia quanto da indústria. Eles publicaram vários artigos sobre segurança em blockchain em conferências de prestígio, relataram vários ataques zero-day em aplicações DeFi e lançaram relatórios detalhados de análise de incidentes de segurança de alto impacto.

Twitter: [BlockSecTeam]

Medium: https://blocksecteam.medium.com

Website: https://www.blocksec.com

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit