Beosin revela vulnerabilidade crítica na instrução IDL no framework Solana Anchor em versões antigas

iconMetaEra
Compartilhar
AI summary iconResumo
A equipe de segurança da Beosin identificou uma vulnerabilidade crítica na instrução IDL em frameworks Solana Anchor de versões antigas. A falha permite que atacantes se apropriem de contas PDA pertencentes a programas explorando declarações de AccountInfo, possibilitando o roubo de fundos em dois passos sem necessidade de privilégios do usuário. O incidente destaca a necessidade de um quadro de conformidade mais robusto no desenvolvimento de contratos inteligentes. Com o MiCA (Regulamento da UE sobre Mercados de Ativos Criptográficos) se aproximando, tais vulnerabilidades reforçam a importância de auditorias de segurança em tempo real e da adesão a padrões regulatórios.

Anchor é o framework mais popular no ecossistema Solana. Por meio de características como validação declarativa de contas, serialização automática e verificações de segurança integradas, reduz significativamente a curva de aprendizado para desenvolvedores. No entanto, enquanto oferece conveniência, o framework injeta silenciosamente em cada programa algumas instruções internas que os desenvolvedores podem não conhecer. Essas instruções "ocultas" podem ser exploradas por atacantes em condições específicas, causando perdas financeiras graves.

A equipe de segurança da Beosin revelará um padrão de vulnerabilidade crítico: em versões antigas do Anchor, quando os desenvolvedores usam AccountInfo para declarar contas PDA pertencentes ao programa, um atacante pode, por meio da instrução IDL injetada automaticamente pelo Anchor, tomar controle da conta e esvaziar todos os SOL nela contidos em apenas duas etapas, sem necessidade de qualquer privilégio.

I. Análise das instruções IDL e dos mecanismos relacionados

1.1 Instrução IDL

O Anchor injeta automaticamente um conjunto de instruções de IDL (Interface Definition Language) em cada programa, a menos que o recurso no-idl seja explicitamente ativado durante a compilação. Essas instruções incluem:

IdlCreateAccount: Criar uma conta IDL na cadeia

IdlWrite: Escrever dados na conta IDL / conta de buffer

IdlSetAuthority: Alterar a autoridade (controlador) da conta IDL

IdlCloseAccount: Fechar a conta IDL e transferir todos os lamports para o destinatário especificado

IdlResizeAccount: Ajustar o tamanho da conta IDL

IdlCreateBuffer: criar conta de buffer IDL (IDL Buffer)

IdlSetBuffer: Substitua a conta IDL oficial com dados da conta de buffer

Esses comandos foram projetados originalmente para gerenciamento de IDL na cadeia, mas possuem capacidades especiais sobre contas possuídas pelo programa (ler/escrever dados, alterar controladores, fechar e transferir lamports) — exatamente a essência da vulnerabilidade. O atacante não precisa chamar qualquer comando de negócio escrito pelo desenvolvedor; pode chamar diretamente esses comandos integrados.

1.2 Conta de buffer IDL

A conta de buffer IDL (IDL Buffer) é uma conta temporária introduzida pelo Anchor para fazer upload segmentado de dados IDL grandes. Como o IDL completo (após compactação em JSON) pode exceder o limite de tamanho de uma única transação, o Anchor permite criar primeiro um buffer por meio de IdlCreateBuffer, depois escrever em partes por meio de múltiplas operações IdlWrite e, finalmente, submeter tudo de uma vez por meio de IdlSetBuffer.

O ponto-chave é a estrutura de dados da conta IDL / conta de buffer: ela começa com um cabeçalho de layout fixo, que contém um campo authority: Pubkey (denominado controller na saída de teste deste artigo). As instruções IDL usam esse campo para determinar “quem tem permissão para operar nessa conta”.

Mas o problema é que a lógica de processamento do IdlCreateBuffer em versões antigas do Anchor trata qualquer conta passada, pertencente ao próprio programa, como uma conta de buffer e define diretamente a autoridade como o signatário da transação. Ou seja, desde que o proprietário de uma conta seja o próprio programa (por exemplo, um PDA de tesouraria do programa), um atacante pode se tornar seu controlador e, em seguida, transferir legalmente todos os SOL da conta usando o IdlCloseAccount.

1.3 Condições de ativação da vulnerabilidade

A exploração da vulnerabilidade requer que as seguintes condições sejam atendidas simultaneamente:

  • Usar versão antiga do Anchor: instruções IDL inseguras não realizam verificação adequada de contas, permitindo que contas de negócios sejam incorretamente tratadas como contas IDL.
  • no-idl não habilitado: o programa manteve a entrada de instrução IDL injetada padrão durante a compilação, expondo a superfície de ataque.
  • Declarar o PDA possuído pelo programa com AccountInfo: os desenvolvedores usam AccountInfo cru para carregar contas de fundos (como o PDA do cofre), faltando o discriminador / proprietário fornecidos pelas contas tipificadas do Anchor (como Account), fazendo com que essa conta pareça “indistinguível” das contas do IDL para as instruções do IDL.
  • A conta é de propriedade do programa e possui lamports: owner == este programa é a condição prévia para que as instruções do IDL possam operá-la; possuir SOL é o que confere valor a ser esvaziado.

Após atender às condições acima, o atacante precisa apenas de duas transações comuns: primeiro IdlCreateBuffer para tomar o controle, depois IdlCloseAccount para transferir todo o SOL, concluindo o ataque sem exigir qualquer permissão do programa alvo.

1.4 Chain of attack

Abaixo, combinando a implementação interna do Anchor, desmontamos passo a passo como o atacante conseguiu, usando apenas duas instruções internas, “fingir” que um vault de fundos comum era uma conta IDL e esvaziá-lo.

Visão geral do fluxo de ataque

Etapa 1: IdlCreateBuffer(tesouraria, signer = atacante)    
└─ treasury.controller ==> atacante (torna-se controlador)  
Etapa 2: IdlCloseAccount(tesouraria, authority = atacante, dest = atacante)  
  └─ treasury.lamports ==> atacante (esvazia a tesouraria)

Primeiro passo: IdlCreateBuffer toma a autoridade

A implementação interna do Anchor é aproximadamente a seguinte:

#[derive(Accounts)]pub struct IdlCreateBuffer {  
#[account(zero)]          // ← Key: uma conta com seu discriminador todo 0  pub buffer: Account,  
pub authority: Signer,}    
pub fn idl_create_buffer(ctx: Context) -> Result 
{ 
 let idl = &mut ctx.accounts.buffer;  idl.authority = *ctx.accounts.authority.key; // definir attack como autoridade  Ok(())}

O significado de #[account(zero)] é: aceitar uma conta cujo discriminator é totalmente zero e que é possuída pelo programa, como uma conta IDL não inicializada para ser inicializada. E o vault satisfaz exatamente essas duas condições:

Estado do vault

Condições

propriedade do programa

após init_if_needed, o proprietário é definido como este programa

discriminator todos os dígitos zero

Ao usar o tipo AccountInfo, o Anchor não grava o discriminator, todos os dados são zero.

Então o atacante passou o vault para o IdlCreateBuffer:

  • Anchor escreve o discriminator do IdlAccount nos 8 primeiros bytes antes do vault;
  • Escreva a chave pública do atacante no campo authority.

Até agora, o vault foi "fingido" como um IdlAccount com o atacante como autoridade—e seus SOL internos permaneceram inalterados.

Passo 2: IdlCloseAccount —— Limpar fundos

#[derive(Accounts)]pub struct IdlCloseAccount {  #[account(mut, has_one = authority)]  // ← check authority == signer  pub account: Account,  pub authority: Signer,  #[account(mut)]  pub destination: AccountInfo,  // ← attack wallet}

O vault já passou por todas as verificações:

  • discriminator corresponde a IdlAccount
  • O campo authority = chave pública do atacante
  • has_one = autoridade verificação aprovada

Portanto, todos os lamports dentro do vault (todo o SOL depositado pelo usuário) foram legalmente transferidos para a conta do atacante, deixando o vault com saldo zero.

Causa raiz:

Usar AccountInfo em deposit.rs em vez de uma Conta Anchor tipada é a causa fatal desta vulnerabilidade:

(1) Contas com tipo (como Account) escrevem um discriminator de 8 bytes exclusivo dessa estrutura durante a inicialização;

(2) Uma vez que o próprio discriminator é gravado, o Anchor não pode mais tratá-lo como IdlAccount (discriminator não corresponde), e o primeiro passo, IdlCreateBuffer, falhará, interrompendo a cadeia de ataque.

Dois, análise de caso

Os testes abaixo são baseados no contrato PoC da ponte construído com o Anchor 0.31.0. O teste simula um cofre de ponte real (Bridge Treasury), cujo proprietário é o próprio programa da ponte, contendo 1,001281 SOL depositados pelos usuários. A carteira do atacante inicialmente possui 2 SOL e não possui nenhum privilégio.

Project

Valor / Descrição

Programa de Ponte

CJutNlr8d3oxdb11ReOLaZd5jPsqUxgwt2mv5e7equtE

Conta do tesouro (Treasury)

DxGkzaMhP5Wy824GhoehErdD6MudfuEPGPwaV2s77FsM

Proprietário da KuCoin

= Program-owned PDA

Saldo inicial da Cofre

1.001281 SOL (depósito do usuário)

Controlador inicial

0x0000...0000 (todos zeros, não definido)

Saldo inicial da carteira do atacante

2.000000 SOL

Privilégios necessários

NENHUM (nenhuma autorização necessária)

Número de transações

2 transações

Saldo final da vault

0.000000 SOL (esgotado)

Atacantes lucraram

+1.001281 SOL

Captura de tela da execução do PoC (tests/poc-idl-hijack.ts):

https://wdcdn.qpic.cn/MTMxMDI3MDE1MTgxMDU0NzA_812435_a37-yrU_dY02i1_I_1785900257?w=1080&h=1287

Pode-se ver que o IdlCreateBuffer do Passo 1 define o atacante como controlador do cofre (o saldo permanece inalterado); o IdlCloseAccount do Passo 2 transfere os 1,001281 SOL do cofre integralmente para a carteira do atacante, deixando o saldo do cofre em zero.

Recomendações de correção/proteção:

(1) Atualize a versão do Anchor: a versão mais recente corrige esse problema (as instruções IDL distinguem estritamente contas IDL de contas de negócios); evitar o uso de versões antigas do Anchor é a defesa mais direta e fundamental.

(2) Habilitar no-idl durante a construção: desativar explicitamente a injeção de instruções IDL para programas de produção, eliminando assim a superfície de ataque desde a fonte.

(3) Use typed accounts instead of raw AccountInfo: Use typed accounts such as Account / SystemAccount with discriminator and owner validation to hold funds accounts, preventing them from being misidentified by IDL instructions.

(4) Minimizar contas esvaziáveis possuídas pelo programa: adicionar restrições explícitas de owner / seeds / discriminator ao PDA que armazena fundos e verificar o discriminador da conta em instruções críticas.

Conclusão

A vulnerabilidade é essencialmente uma combinação de “instrução oculta pelo framework + ausência de verificação de tipo de conta”. Os desenvolvedores devem reconhecer que as instruções IDL injetadas padrão pelo Anchor representam uma superfície de ataque real; atualizar a versão do framework e aplicar restrições de tipo forte às contas de fundos pode efetivamente eliminar esse risco de esvaziamento não autorizado de fundos.

Beosin é uma empresa líder em segurança blockchain e tecnologia de conformidade regulatória, especializada em auditoria de segurança de contratos inteligentes antes do lançamento de projetos, monitoramento e bloqueio de riscos de segurança durante a operação dos projetos, recuperação de ativos roubados, combate à lavagem de dinheiro (AML) para ativos virtuais e investigação e rastreamento. A Beosin já forneceu produtos e serviços de conformidade blockchain “tudo-em-um” para agências regulatórias e de aplicação da lei em mais de 20 países e regiões, mais de 200 provedores de ativos virtuais e 4.500 projetos Web3. Entre em contato conosco por meio da caixa de mensagens do nosso canal oficial.

Aviso legal: as informações nesta página podem ter sido obtidas de terceiros e não refletem necessariamente os pontos de vista ou opiniões da KuCoin. Este conteúdo é fornecido apenas para fins informativos gerais, sem qualquer representação ou garantia de qualquer tipo, nem deve ser interpretado como aconselhamento financeiro ou de investimento. A KuCoin não é responsável por quaisquer erros ou omissões, ou por quaisquer resultados do uso destas informações. Os investimentos em ativos digitais podem ser arriscados. Avalie cuidadosamente os riscos de um produto e a sua tolerância ao risco com base nas suas próprias circunstâncias financeiras. Para mais informações, consulte nossos termos de uso e divulgação de risco.