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
Como um Bug Crítico na Rede Solana Foi Detectado e Corrigido a Tempo