Autor: CertiK
Anteriormente, a equipe CertiK descobriu uma série de vulnerabilidades de negação de serviço no blockchain Sui. Entre essas vulnerabilidades, destaca-se uma vulnerabilidade nova e de alto impacto. Esta vulnerabilidade pode fazer com que os nós da rede Sui não consigam processar novas transações, o que equivale ao desligamento completo de toda a rede.
Na segunda-feira passada, a CertiK recebeu uma recompensa de bug SUI de US$ 500.000 por descobrir esta grande vulnerabilidade de segurança. CoinDesk, a mídia autorizada na indústria dos EUA, informou sobre o incidente e, em seguida, a grande mídia acompanhou seu relatório e divulgou notícias relevantes.
Essa vulnerabilidade de segurança é chamada de "roda de hamster": seu método de ataque exclusivo é diferente dos ataques atualmente conhecidos. O invasor só precisa enviar uma carga útil de cerca de 100 bytes para acionar um loop infinito no nó de verificação Sui, tornando-o incapaz. para responder a novas transações.
Além disso, os danos causados pelo ataque podem persistir após a reinicialização da rede e se propagar automaticamente na rede Sui, deixando todos os nós como um hamster correndo indefinidamente na roda, incapazes de processar novas transações. É por isso que chamamos esse tipo de ataque único de ataque de “roda de hamster”.

Depois de descobrir a vulnerabilidade, a CertiK relatou a Sui por meio do programa de recompensas de bugs da Sui. Sui também respondeu de forma eficaz o mais rápido possível, confirmou a gravidade da vulnerabilidade e tomou ativamente as medidas correspondentes para reparar o problema antes do lançamento da rede principal. Além de corrigir esta vulnerabilidade específica, Sui implementou mitigações preventivas para reduzir os danos potenciais que esta vulnerabilidade poderia causar.
Para agradecer à equipe CertiK pela divulgação responsável, Sui concedeu à equipe CertiK um bônus de US$ 500.000.
Os seguintes detalhes técnicos desta vulnerabilidade crítica serão divulgados para esclarecer a causa raiz e o impacto potencial da vulnerabilidade.
Explicação detalhada das vulnerabilidades
O papel fundamental dos validadores no Sui
Para blockchains baseados na linguagem Move, como Sui e Aptos, seu mecanismo de proteção contra ataques maliciosos de carga útil é principalmente tecnologia de verificação estática. Por meio da tecnologia de verificação estática, Sui pode verificar a validade da carga enviada pelos usuários antes do lançamento ou atualização do contrato. O validador disponibiliza uma série de verificadores para garantir a exatidão da estrutura e da semântica. Somente após passar nas verificações e verificações, o contrato entrará na máquina virtual Move para execução.

Ameaças de carga útil maliciosa na cadeia Move
A cadeia Sui fornece um novo conjunto de modelos de armazenamento e interfaces sobre a máquina virtual Move original, portanto, Sui tem uma versão personalizada da máquina virtual Move. Para oferecer suporte às novas primitivas de armazenamento, Sui introduz ainda uma série de verificações adicionais e personalizadas para verificação de segurança de cargas não confiáveis, como segurança de objetos e acesso ao armazenamento global. Essas verificações personalizadas atendem aos recursos exclusivos do Sui, por isso chamamos essas verificações personalizadas de validadores Sui.

Ordem de Sui para verificar a carga
Conforme mostrado na figura acima, a maioria das verificações no validador realiza verificação de segurança em nível estrutural em relação ao CompiledModule (que representa a execução da carga útil do contrato fornecida pelo usuário). Por exemplo, use o "Verificador de Duplicados" para garantir que não haja entradas duplicadas na carga de tempo de execução; use o "Verificador de Limite" para garantir que o comprimento de cada campo na carga de tempo de execução esteja dentro do limite de entrada permitido.
Além das verificações no nível estrutural, a verificação estática do verificador ainda requer métodos de análise mais complexos para garantir a robustez da carga útil não confiável no nível semântico.
Aprenda sobre o intérprete abstrato do Move:
Análise linear e iterativa
O interpretador abstrato fornecido pelo Move é uma estrutura projetada especificamente para realizar análises complexas de segurança em bytecode por meio de interpretação abstrata. Este mecanismo torna o processo de verificação mais refinado e preciso, e cada verificador pode definir seu estado abstrato único para análise.
No início de uma execução, o interpretador abstrato constrói um gráfico de fluxo de controle (CFG) a partir dos módulos compilados. Cada bloco básico nestes CFG mantém um conjunto de estados, nomeadamente "estado de pré-encomenda" e "estado de pós-encomenda". O "estado de pré-ordem" fornece um instantâneo do estado do programa antes da execução do bloco básico, enquanto o "estado de pós-ordem" fornece uma descrição do estado do programa após a execução do bloco básico.
Quando o interpretador abstrato não encontra saltos (ou loops) no gráfico de fluxo de controle, ele segue um princípio de execução linear simples: cada bloco básico é analisado por vez e a instrução anterior é calculada com base na semântica de cada instrução no bloco. estado sequencial e estado pós-sequencial. O resultado é um instantâneo preciso de cada estado básico de nível de bloco de um programa durante a execução, ajudando a verificar as propriedades de segurança do programa.

Mover fluxo de trabalho do intérprete abstrato
Porém, esse processo se torna mais complicado quando há loops no fluxo de controle. A ocorrência de um loop significa que o gráfico de fluxo de controle contém uma borda de rejeição. A origem da borda de rejeição corresponde ao estado subsequente do bloco básico atual, e o bloco básico de destino (cabeça do loop) da borda de rejeição é previamente analisado. O estado de pré-ordem do bloco básico, portanto, o intérprete abstrato precisa mesclar cuidadosamente os estados dos dois blocos básicos relacionados ao salto.
Se o estado mesclado for diferente do estado de pré-ordem existente do bloco básico da cabeça do loop, o interpretador abstrato atualiza o estado do bloco básico da cabeça do loop e reinicia a análise a partir deste bloco básico. Este processo de análise iterativa continua até que o pré-estado do loop esteja estável. Em outras palavras, esse processo é repetido até que o estado de pré-ordem do bloco básico no início do loop não mude mais entre as iterações. Alcançar um ponto fixo indica que a análise do loop foi concluída.
Validador Sui IDLeak:
Análise de interpretação abstrata personalizada
Ao contrário do design original do Move, a plataforma blockchain da Sui apresenta um modelo de armazenamento global exclusivo centrado em “objetivos”. Uma característica notável deste modelo é que qualquer estrutura de dados com um atributo chave (armazenado como um índice na cadeia) deve ter o tipo ID como o primeiro campo da estrutura. O campo ID é imutável e não pode ser transferido para outros destinos porque cada objeto deve ter um ID globalmente exclusivo. Para garantir essas propriedades, Sui construiu um conjunto de lógica de análise personalizada sobre o interpretador abstrato.

O verificador IDLeak, também conhecido como id_leak_verifier, funciona em conjunto com o interpretador abstrato para realizar análises. Ele possui seu próprio AbstractDomain exclusivo, chamado AbstractState. Cada AbstractState consiste em AbstractValue correspondente a múltiplas variáveis locais. Monitore o status de cada variável local por meio de AbstractValue para rastrear se uma variável de ID é nova.
Durante o processo de empacotamento da estrutura, o validador IDLeak permite apenas que um novo ID seja empacotado em uma estrutura. Através da análise de interpretação abstrata, o validador IDLeak pode rastrear exaustivamente o estado do fluxo de dados local para garantir que nenhum ID existente seja transferido para outros objetos de estrutura.
Problema de inconsistência de manutenção do estado do validador Sui IDLeak
O validador IDLeak é integrado ao interpretador abstrato Move implementando a função AbstractState::join. Esta função desempenha um papel integral na gestão do estado, especialmente na fusão e atualização dos valores do estado.
Examine essas funções em detalhes para entender sua operação:

Em AbstractState::join, a função recebe outro AbstractState como entrada e tenta mesclar seu estado local com o estado local do objeto atual. Para cada variável local no estado de entrada, ele compara o valor da variável com seu valor atual no estado local (se não for encontrado, o padrão é AbstractValue::Other). Se os dois valores não forem iguais, ele definirá um sinalizador "alterado" como base para saber se o resultado final da mesclagem do estado foi alterado e atualizará o valor da variável local no estado local chamando AbstractValue::join.

Em AbstractValue::join, a função compara seu valor com outro AbstractValue. Se forem iguais, retornará o valor passado. Se não for igual, AbstractValue::Other será retornado.
No entanto, esta lógica de manutenção de estado contém um problema de inconsistência oculto. Embora AbstractState::join retorne um resultado indicando que o estado mesclado foi alterado (JoinResult::Changed) com base na diferença entre os valores antigos e novos, o valor do estado atualizado mesclado ainda pode permanecer inalterado.
Este problema de inconsistência é causado pela ordem das operações: a determinação do estado alterado em AbstractState::join ocorre antes da atualização do estado (AbstractValue::join), e esta determinação não reflete o resultado da atualização do estado real.
Além disso, em AbstractValue::join, AbstractValue::Other desempenha um papel decisivo no resultado da fusão. Por exemplo, se o valor antigo for AbstractValue::Other e o novo valor for AbstractValue::Fresh, o valor do estado atualizado ainda será AbstractValue::Other, mesmo que os valores antigos e novos sejam diferentes, o próprio estado não mudar após a atualização.

Exemplo: Incoerência nas conexões de estado
Isso introduz uma inconsistência: o resultado da fusão dos estados básicos do bloco é considerado "alterado", mas o valor do estado mesclado em si não mudou. No processo de interpretação e análise abstrata, a ocorrência de tais inconsistências pode ter consequências graves. Revisamos o comportamento do interpretador abstrato quando ocorrem loops em um gráfico de fluxo de controle (CFG):
Quando um loop é encontrado, o interpretador abstrato usa um método de análise iterativo para mesclar o estado do bloco básico de destino do salto com o bloco básico atual. Se o estado mesclado mudar, o interpretador abstrato irá reanalisar a partir do destino do salto.
No entanto, se a operação de mesclagem da análise de interpretação abstrata marcar erroneamente o resultado da mesclagem do estado como "alterado", quando na verdade o valor da variável interna do estado não mudou, isso levará a uma reanálise interminável e criará um loop infinito .
explorando ainda mais a inconsistência
Acionar loop infinito no validador Sui IDLeak
Explorando essa inconsistência, um invasor pode construir um gráfico de fluxo de controle malicioso para enganar o validador IDLeak em um loop infinito. Este gráfico de fluxo de controle cuidadosamente construído consiste em três blocos básicos: BB1 e BB2, BB3. É importante notar que introduzimos intencionalmente uma borda de salto de BB3 para BB2 para construir um loop.

O status CFG+ malicioso pode levar a um loop infinito dentro do validador IDLeak.
O processo começa com BB2, onde o AbstractValue de uma variável local específica é definido como ::Other. Após executar o BB2, o processo é transferido para o BB3, onde a mesma variável é definida como ::Fresh. No final do BB3, há uma borda de salto que salta para o BB2.
No processo de interpretação e análise abstrata deste exemplo, a inconsistência mencionada anteriormente desempenha um papel fundamental. Quando a borda de rejeição é processada, o interpretador abstrato tenta conectar o estado de pós-pedido do BB3 (a variável é "::Fresh") com o estado de pré-pedido do BB2 (a variável é "::Other"). A função AbstractState::join percebeu a diferença entre os valores antigos e novos e definiu o sinalizador "change" para indicar que o BB2 precisa ser reanalisado.
No entanto, o comportamento dominante de "::Other" em AbstractValue::join significa que após a mesclagem de AbstractValue, o valor real da variável de estado BB2 ainda é "::Other" e o resultado da mesclagem de estado não mudou.
Assim, uma vez iniciado este processo cíclico, ou seja, à medida que o validador continua a reanalisar o BB2 e todos os seus nós de bloco básicos sucessores (BB3 neste caso), ele continua indefinidamente. O loop infinito consome todos os ciclos de CPU disponíveis, impossibilitando o processamento de respostas a novas transações, e esta situação persiste após a reinicialização do validador.
Ao explorar esta vulnerabilidade, os nós validadores funcionam em um loop infinito, como um hamster correndo indefinidamente em uma roda, incapaz de processar novas transações. É por isso que chamamos esse tipo de ataque único de ataque de “roda de hamster”.
Um ataque de "roda de hamster" pode efetivamente paralisar o validador Sui, paralisando assim toda a rede Sui.
Depois de entender a causa e o processo de acionamento da vulnerabilidade, construímos um exemplo específico usando a seguinte simulação de Move bytecode e acionamos com sucesso a vulnerabilidade na simulação no ambiente real:

Este exemplo mostra como acionar a vulnerabilidade em um ambiente real por meio de bytecode cuidadosamente construído. Especificamente, um invasor pode acionar um loop infinito no validador IDLeak, usando uma carga útil de apenas cerca de 100 bytes para consumir todos os ciclos de CPU de um nó Sui, impedindo efetivamente o processamento de novas transações e causando uma negação de serviço na rede Sui. .
Os danos persistentes dos ataques de “roda de hamster” na rede Sui
O programa de recompensas por bugs da Sui possui regulamentações rígidas sobre a avaliação dos níveis de vulnerabilidade, principalmente com base no grau de dano a toda a rede. Uma vulnerabilidade que atenda à classificação "crítica" deve desligar toda a rede e impedir efetivamente a confirmação de novas transações, e requer um hard fork para corrigir o problema, se a vulnerabilidade só puder fazer com que alguns nós da rede neguem o serviço, será; classificados como “risco médio” no máximo (médio)” ou vulnerabilidade “alta”.
A vulnerabilidade da “roda de hamster” descoberta pela equipe CertiK Skyfall pode desligar toda a rede Sui e requer o lançamento oficial de uma nova versão para atualizá-la e corrigi-la. Com base na gravidade da vulnerabilidade, Sui classificou-a como “crítica”. Para compreender melhor o sério impacto do ataque da “roda de hamster”, precisamos compreender a arquitetura complexa do sistema backend de Sui, especialmente todo o processo de liberação ou atualização de transações na cadeia.

Visão geral da interação para envio de transações no Sui
Inicialmente, as transações do usuário são enviadas via RPC front-end e repassadas ao serviço back-end após verificação básica. O serviço de back-end Sui é responsável por validar ainda mais a carga útil da transação recebida. Após a verificação bem-sucedida da assinatura do usuário, a transação é convertida em um certificado de transação (contendo as informações da transação, bem como a assinatura de Sui).
Esses certificados de transação são parte fundamental da operação da rede Sui e podem ser propagados entre vários nós de verificação da rede. Para transações de criação/atualização de contrato, o nó de verificação chamará o validador Sui para verificar e verificar a validade da estrutura/semântica do contrato desses certificados antes que eles possam ser colocados na cadeia. É durante esta fase crítica de verificação que a vulnerabilidade do “loop infinito” pode ser acionada e explorada.
Quando a vulnerabilidade é acionada, faz com que o processo de verificação seja interrompido indefinidamente, prejudicando efetivamente a capacidade do sistema de processar novas transações e causando o desligamento completo da rede. Para piorar a situação, a situação ainda existe após o nó ser reiniciado, o que significa que as medidas tradicionais de mitigação estão longe de ser suficientes. Assim que esta vulnerabilidade for acionada, ocorrerão “danos contínuos”, deixando um impacto duradouro em toda a rede Sui.
A solução de Sui
Após feedback da CertiK, Sui confirmou prontamente a vulnerabilidade e lançou uma correção para resolver a falha crítica. A correção garante consistência entre mudanças de estado e sinalizadores pós-mudança, eliminando o impacto crítico de ataques de “roda de hamster”.

Para eliminar a inconsistência acima, a correção de Sui inclui um ajuste pequeno, mas crítico, na função AbstractState::join. Este patch remove a lógica de determinar o resultado da fusão de estados antes de executar AbstractValue::join. Em vez disso, ele primeiro executa a função AbstractValue::join para realizar a fusão de estados e define se a fusão ocorre comparando o resultado final da atualização com o estado original. valor (old_value). Marca de alteração.
Desta forma, o resultado da fusão de estados será consistente com o resultado da atualização real e nenhum loop infinito ocorrerá durante o processo de análise.
Além de corrigir esta vulnerabilidade específica, Sui implantou mitigações para reduzir o impacto de futuras vulnerabilidades do validador. De acordo com a resposta de Sui no relatório do bug, a mitigação envolve um recurso chamado Denylist.
"No entanto, os validadores possuem um arquivo de configuração de nó que lhes permite rejeitar temporariamente certas categorias de transações. Esta configuração pode ser usada para desabilitar temporariamente o processamento de lançamentos e atualizações de pacotes. Devido a este bug é necessário executar Sui antes de assinar um lançamento ou pacote atualizar tx enquanto uma lista de negação interromperá a execução do validador e eliminará o tx malicioso, negar temporariamente a listagem desses tipos de tx é uma mitigação 100% eficaz (embora interrompa temporariamente o serviço para qualquer pessoa que tente liberar ou atualizar o código).
A propósito, já há algum tempo que temos esse arquivo de configuração de lista de negações de TX, mas também adicionamos um mecanismo semelhante para certificados como uma mitigação de acompanhamento para a vulnerabilidade de "loop infinito do validador" que você relatou anteriormente. Com esse mecanismo implementado, teremos mais flexibilidade com esse ataque: usaremos a configuração da lista de negação de certificados para fazer o validador esquecer os certificados ruins (quebrando o loop infinito) e a configuração da lista de negação de TX para desabilitar lançamentos/atualizações, evitando assim a criação de novas transações de ataques maliciosos. Obrigado por nos fazer pensar sobre isso!
Um validador possui um número limitado de "ticks" (ao contrário do gas) para verificação do bytecode antes de assinar uma transação, se todo o bytecode emitido em uma transação não puder ser verificado em tantos ticks, o validador se recusará a assinar a transação, impedindo-a de executando na rede. Anteriormente, a medição funcionava apenas para um conjunto selecionado de passagens complexas do validador. Para lidar com este problema, estendemos a medição a cada validador para garantir que haja uma restrição no trabalho realizado pelo validador durante o processo de verificação de cada tick. Também corrigimos um possível bug de loop infinito no validador de vazamento de ID. "
--Instruções dos desenvolvedores Sui sobre correções de bugs
Resumindo, a Denylist permite que os validadores contornem temporariamente explorações nos validadores e evitem efetivamente danos potenciais causados por algumas transações maliciosas, desativando o processo de lançamento ou atualização. Quando as medidas de mitigação da lista de bloqueio entram em vigor, os nós garantem que podem continuar a trabalhar sacrificando suas próprias funções de contrato de publicação/atualização.

Resumir
Neste artigo, compartilhamos os detalhes técnicos do ataque “roda de hamster” descoberto pela equipe CertiK Skyfall, explicando como esse novo ataque explora vulnerabilidades importantes para fazer com que a rede Sui seja completamente desligada. Além disso, também analisamos mais de perto a resposta oportuna de Sui para corrigir esse problema crítico e compartilhamos a correção da vulnerabilidade e os métodos de mitigação subsequentes para vulnerabilidades semelhantes.
