A apenas 111 bytes da falha: como uma vulnerabilidade no HOT Omni Bridge levou a uma perda de $3.86M

A apenas 111 bytes da falha: como uma vulnerabilidade no HOT Omni Bridge levou a uma perda de $3.86M

Os fatos apontam para uma possível discrepância entre o código-fonte público corrigido e a versão do contrato realmente em execução na rede.

12 de out. de 2026

O invasor usou um saldo grande, previamente acumulado, de um token auxiliar especial para solicitar reembolsos muito superiores ao valor de um novo depósito. A gravação dos valores numéricos enormes resultantes fez com que o evento de reembolso excedesse o limite de log da NEAR de 16,384 bytes. Por causa disso, o manipulador de depósitos do Intents não conseguiu concluir a operação. Após a falha, o HOT devolveu os ativos originais que respaldavam o depósito, incluindo USDT, mas o saldo interno do Intents creditado por uma operação bem-sucedida anterior permaneceu em vigor.

Endereços e suas funções

RedeEndereço ou contaFunção
BSC0x09fd1f5d9f185067a92493e43aa259ea4ab3ad37O invasor, que recebeu cerca de $3.865M
BSC0x233c5370CCfb3cD7409d9A3fb98ab94dE94Cb4CdTesouro do HOT Bridge
NEAR20e6bfe4685d99004fc2095d3a0fc6158cd810ce8f801196141e183ed6e19d65A conta do invasor, a partir da qual os depósitos em lote e as solicitações de saque foram iniciados; também a destinatária dos ativos devolvidos que respaldavam o depósito
NEARdd1fe58ec316ada5b673b88c14fe2fa2b7ede82080440901dfbd64b994241757O contrato do invasor, destinatário do saldo interno e contrato de callback para notificações
NEAR873518f0f5cf120d7a8e2a416337bba16d7ae622e91f7da3862f07e89dc2ba9eO contrato do invasor que se tornou o destinatário do saldo interno nas sequências de operações subsequentes de 1 de outubro que puderam ser confirmadas. Forma abreviada: 8735…
NEARv2_1.omni.hot.tgO contrato da ponte HOT e o registro das representações multi-token NEP-245
NEARintents.nearO contrato verificador do Intents e o registro interno dos saldos dos usuários

Contexto

O HOT Bridge, também chamado de HOT Omni Bridge, conecta ativos de blockchains externas à rede NEAR e está integrado ao NEAR Intents para trocas de tokens. O SDK público do projeto é @hot-labs/omni-sdk.

O sistema envolve três níveis de contabilidade: o ativo real na blockchain externa, mantido no tesouro do HOT → a representação desse ativo emitida pelo HOT na NEAR → o saldo interno do usuário no intents.near.

Um depósito normal de entrada passa pelas seguintes etapas:

  • O usuário deposita um ativo real na blockchain de origem.
  • O HOT verifica o depósito e autoriza a representação correspondente do ativo na NEAR.
  • O HOT emite (mint) os tokens correspondentes na NEAR. Esses tokens são transferidos para o Intents, onde o valor correspondente é refletido no saldo interno do usuário.

O código de processamento de depósitos do Intents também oferece suporte a uma notificação opcional ao destinatário. O sistema primeiro credita os fundos na conta e, em seguida, chama a aplicação destinatária. Essa aplicação pode solicitar o reembolso de parte do valor depositado.

Em um reembolso normal, o Intents deve primeiro remover o valor correspondente do saldo interno e, em seguida, devolver os ativos que respaldam esse saldo. O manipulador queima (burn) o crédito não utilizado, ou seja, cancela a parte correspondente do saldo interno, e informa ao HOT quais valores permaneceram não utilizados. O HOT então devolve os tokens correspondentes ao remetente original.

Durante o incidente, o vínculo entre o segundo e o terceiro níveis de contabilidade se rompeu: o HOT devolveu o respaldo do depósito como tokens de representação na NEAR, mas o Intents manteve o saldo interno do usuário. Isso posteriormente possibilitou solicitar saques do tesouro por meio do mecanismo normal de saque da ponte.

1. Preparação

Em 29 de setembro, o invasor recebeu 10 USDT da BSC na NEAR na transação 9SiSBv….

O invasor também transferiu da rede Gonka 97 × 10³⁶ + 97 unidades mínimas do ativo auxiliar especial ctfhote6ae2a90d1836ef1. O depósito original corresponde ao recebimento posterior do ativo na NEAR na transação Fmwao…. Como resultado, tokens de representação do HOT foram emitidos para o endereço 20e6….

Em seguida, na transação 4wf2…, o invasor depositou 97 × 10³⁶ unidades do ativo auxiliar no Intents para o contrato destinatário do ataque dd1…. 97 unidades mínimas permaneceram fora do Intents. Isso criou o grande saldo interno necessário para as solicitações de reembolso superdimensionadas posteriores.

2. Exploração da vulnerabilidade

A primeira operação de exploração confirmada, 3k91…, começou em 29 de setembro às 16:14:19.280 UTC.

O invasor chamou a função HOT mt_batch_transfer_call, depositando no Intents 97 entradas do token auxiliar de uma unidade cada, junto com 10 USDT. O Intents creditou com sucesso os fundos ao saldo interno de dd1... antes de notificar o contrato destinatário controlado pelo invasor. O contrato destinatário solicitou reembolsos de 97 quantias do token auxiliar, de 10³⁶ unidades cada, e então indicou que nenhum reembolso em USDT era necessário.

O manipulador de reembolso vulnerável limitava o valor do reembolso ao saldo disponível, mas não considerava o tamanho do depósito atual. Como resultado, o saldo previamente preparado possibilitou incluir valores enormes de reembolso no evento de queima do token.

Aqui, a variável balance_left representa todo o saldo interno do destinatário em um determinado ativo, incluindo depósitos anteriores. A verificação determinava se o destinatário tinha fundos suficientes no saldo, mas não limitava o reembolso ao valor depositado na operação atual.

Como resultado, uma entrada de depósito de apenas uma unidade podia gerar uma solicitação de reembolso de 10³⁶ unidades, caso o saldo previamente acumulado do destinatário pudesse cobrir esse valor. Cada valor de reembolso diferente de zero calculado era então adicionado ao evento de queima do token, nas linhas 198-199 do código acima.

O tamanho do evento resultante foi de 16,495 bytes:

ComponenteCálculoBytes
97 IDs completos de token, 125 caracteres cada97 × (125 caracteres + 2 aspas) + 96 vírgulas12,415
97 valores de reembolso, cada um uma string entre aspas de 37 dígitos97 × (37 dígitos + 2 aspas) + 96 vírgulas3,879
Prefixo EVENT_JSON, metadados, ID do proprietário, memo e outros caracteres JSONTamanho fixo201
Total12,415 + 3,879 + 20116,495

O erro na formação do evento reverteu as alterações de saldo anteriores feitas pelo manipulador de reembolso. O HOT então devolveu todo o lote de depósito original, incluindo os 10 USDT. O manipulador de depósito havia chamado self.deposit(...) antes de agendar a notificação ao destinatário e a chamada subsequente a mt_resolve_deposit.

Assim, a falha de uma chamada posterior não desfaz automaticamente as alterações feitas com sucesso em uma chamada anterior. Esse comportamento está descrito na documentação do NEAR. O saldo interno do Intents creditado durante a etapa anterior bem-sucedida permaneceu em vigor.

3. Ampliando a escala

Em uma das operações posteriores, 3mst2uZ32KPuCs7wc8rJqEtCrZqugCy4yDSZ3df8KPm3, o invasor depositou 649,984 USDT e solicitou um reembolso exatamente do mesmo valor ao remetente.

O histórico de transações reconstruído contém 23 chamadas em lote vinculadas a falhas no manipulador de reembolso. O padrão completo, em que o saldo interno foi retido enquanto o suporte do depósito foi devolvido, foi confirmado de forma confiável em seis casos.

Problema de sincronização: como o protocolo Verus perdeu $7.5M devido a diferenças entre blockchains3 de ago. de 2026Leia mais

4. Retirando os fundos

O invasor então enviou solicitações de retirada do NEAR através do HOT, especificando o endereço BSC 0x09fd…ad37 como destinatário. Cinco solicitações correspondem a pagamentos de USDT bem-sucedidos do tesouro do HOT na BSC em 0x233c…4Cd.

A vulnerabilidade

1. O saldo interno é creditado antes de o destinatário responder à solicitação de reembolso

A implementação do Intents para lidar com depósitos recebidos primeiro chama:

self.deposit( receiver_id.clone(), core_token_ids .clone() .zip(amounts.iter().map(|amount| amount.0)), Some(«deposit»), ) .unwrap_or_else(|err| err.panic());

Para um depósito com notificação, o sistema agenda então uma chamada ao contrato do destinatário e, em seguida, uma chamada a mt_resolve_deposit. A etapa que cria o saldo interno pode ser concluída com sucesso antes que o manipulador de reembolso subsequente gere um erro. O resultado final da execução informa à HOT que a operação posterior falhou, mesmo que o saldo interno criado anteriormente já exista.

2. O cálculo vulnerável permite um reembolso maior do que o depósito atual

O commit 35044fd… da NEAR, de 1 de outubro, intitulado «chore: TEMP change that reflects deployed state», reproduz a lógica de reembolso da seguinte forma:

let requested_refund = requested_refund.unwrap_or(deposited); let balance_left = receiver.token_balances.amount_for(&token_id); let refund_amount = balance_left.min(requested_refund);*

Fonte: resolve_deposit_internal.

Esse cálculo leva em conta o saldo disponível, mas não limita o reembolso ao valor do depósito atual. O loop adiciona cada valor de reembolso não nulo ao evento de burn, ao mesmo tempo em que o subtrai do saldo do destinatário e do fornecimento total interno do token. Depois disso, é feita a seguinte chamada:

MtEvent::MtBurn([burn_event].as_slice().into()).emit();

Essas alterações contábeis são feitas dentro do recibo do manipulador de reembolso. Se a gravação do evento falhar, todas as alterações feitas nesse recibo são revertidas junto com ele.

3. Uma possível discrepância entre o código publicado e a versão implantada

A falta de limite do valor de reembolso ao tamanho do depósito atual foi corrigida no repositório público em 23 de março de 2026, às 12:47:07 UTC. A correção foi publicada no PR #240 / commit 015fa811…, intitulado «fix: cap notify refunds by the original amounts». Na versão corrigida, o cálculo é assim:

let refund_amount = requested_refund.min(deposited).min(balance_left);*

Para as entradas auxiliares de uma unidade usadas no ataque, isso limita cada reembolso a no máximo uma unidade, independentemente do saldo acumulado anteriormente. Esse mecanismo impede que os valores de reembolso sejam multiplicados por números excessivamente grandes. Também restaura a regra fundamental da contabilidade de depósitos: um reembolso referente à operação atual não pode retirar fundos depositados em depósitos anteriores.

O commit temporário de 1 de outubro remove esse limite para reproduzir o comportamento da versão implantada. A observação do teste de regressão de outubro da NEAR afirma explicitamente que eventos de reembolso com valores excessivos fizeram o callback falhar «como aconteceu na versão implantada».

Resposta e recuperação

Após o incidente, foi realizada uma atualização, refletida na transação FPLaCtd…. A transação principal, executada em 2 de outubro às 20:31:41.711 UTC, chama ctl-intents.near.upgrade para o contrato intents.near. O evento do controlador lista a versão de lançamento 0.4.4.

Um recibo de implantação aninhada bem-sucedido, registrado às 20:31:45.442 UTC, implantou o código e executou a função state_migrate. Alex Shevchenko declarou publicamente que o valor total de $3.86M havia sido devolvido.

#Tether#Ataque hacker#DeFi
USDT foi devolvido, mas o saldo nunca foi debitado: como o HOT Omni Bridge foi atacado