Eu explico o bug depositnotification que gerou US$ 3,8 milhões em fundos fantasma no NEAR Intents, como o ataque funcionou e como a equipe recuperou 100% deles. Um postmortem independente baseado no relatório oficial e na atividade do GitHub.
Em 16 de outubro, Alex Shevchenko publicou o postmortem oficial do exploit NEAR Intents do início deste mês.
Post no X: https://x.com/i/status/2111124226962870770
Um atacante gerou US$ 3,8 milhões em “fundos fantasma” dentro do contrato intents.near e os retirou como tokens reais através da ponte Omni. O recurso por trás do bug já foi removido e 100% dos fundos foram recuperados.

Sim. Tudo.
Hacks de cripto quase nunca terminam assim. Na maioria das vezes, os fundos desaparecem para sempre, ou são parcialmente recuperados após longas negociações e os usuários arcam com a perda. Este foi um trabalho excepcional da equipe e um feito bastante único na segurança de cripto.
Mas o que exatamente aconteceu, como o ataque funcionou e como a equipe conseguiu recuperar tudo?
Este artigo é útil para quem deseja entender o incidente e aumentar seu conhecimento sobre NEAR:
- usuários e entusiastas de NEAR
- desenvolvedores de NEAR e desenvolvedores de blockchain
- pesquisadores de segurança, auditores de contratos inteligentes e caçadores de bug bounty
Como de costume, ele terá uma perspectiva técnica, uma explicação mais fácil e algumas perspectivas focadas em segurança. Desenvolvedores que desejam se aprofundar podem precisar consultar o repositório near/intents e a documentação do NEAR também.
Este artigo também faz parte da minha série de segurança NEAR. Se você perdeu o anterior, escrevi sobre outro vetor de ataque específico do NEAR aqui:
Post no X: https://x.com/i/status/2074951770984640796
Aviso: Eu opero um validador NEAR como thecoding.pool.near e sou um colaborador ativo do ecossistema NEAR. Este artigo NÃO é patrocinado e esta é minha análise independente, construída a partir do postmortem oficial, do histórico público do repositório near/intents e da documentação do protocolo NEAR. Onde o relatório oficial não especifica a mecânica exata, eu a rotulo como reconstrução. Como de costume, leve tudo o que digo com um grão de sal e sinta-se à vontade para participar da discussão nos comentários.
TL;DR Explicado Facilmente
- um atacante criou um contrato no NEAR projetado para falhar de propósito
- eles fizeram 23 depósitos no NEAR Intents ao longo de vários dias, cada um pedindo ao contrato para “notificar” seu contrato falho
- todas as vezes, o NEAR Intents creditou seu saldo interno, a notificação falhou e o contrato do token reembolsou seus tokens
- mas o crédito interno permaneceu -> US$ 3,8 milhões em saldo sem lastro (fundos fantasma)
- de 29 de setembro, 16h14, a 1º de outubro, 05h54, eles retiraram os fundos fantasma como tokens reais através da ponte Omni
- o monitoramento estava observando a ponte, não a solvência do próprio contrato, então nenhum alarme por dias
- uma vez identificado, o contrato foi pausado em 29 minutos
- hotfix lançado em menos de dois dias, recurso removido completamente em 7 de outubro
- 100% dos fundos devolvidos em 2 de outubro
NEAR Intents e os Três Saldos
NEAR Intents é um protocolo de intents multichain. Você deposita tokens de (quase) qualquer cadeia, expressa uma intent como “trocar meu ETH por ZEC” ou “enviar USDC para Solana”, e os resolvedores competem para cumpri-la. Tudo é liquidado dentro do contrato Verifier em intents.near (também chamado de defuse, um nome legado que está sendo descontinuado).
Os fundos entram e saem através da infraestrutura Omni (uma ponte). Importante notar: o postmortem oficial é claro que o bug estava no contrato Intents, não na Omni. Então, para entender o exploit, você precisa saber que cada ativo em ponte existe em três lugares ao mesmo tempo:

Captain’s Club: Networking Blockchain
Captain’s Club é um clube de networking da indústria Blockchain que conecta CEOs e especialistas no Brasil, América Latina, Europa e EUA.
bitcoinblock.com.br
1. tokens na cadeia de origem, bloqueados em tesourarias da ponte -> total X
2. o token NEP141 espelhado no NEAR -> oferta total X
3. tokens realmente mantidos pelo contrato Intents -> Y, onde Y < X (outras contas NEAR detêm o restante)
Além disso, o Verifier mantém seu próprio registro interno com o saldo de cada usuário. Basicamente, um banco privado dentro do contrato.
E há uma regra (um invariante) que deve sempre ser mantida:
sum of all internal user balances <= ft_balance_of(intents.near) // what the ledger says users own must never exceed what the contract actually holdsO extrato bancário não pode prometer mais dinheiro do que o que existe no cofre. E esse é exatamente o gap que o atacante fabricou.
O Bug Explicado Facilmente
Pense em intents.near como uma agência bancária.
(os valores abaixo são ilustrativos, não os valores reais por depósito)
Você entra com US$ 100 mil em dinheiro. O caixa (ft_on_transfer) imediatamente escreve +US$ 100 mil no seu livro-razão. Então, como uma cortesia que você pediu, o banco envia um mensageiro para entregar um “aviso de depósito” a um endereço de sua escolha (a notificação).
A questão é: o endereço é seu, e você nunca abre a porta.
A política do banco diz que um aviso falho significa que o dinheiro volta para o depositante. Então o mensageiro devolve seus US$ 100 mil a você na porta (o reembolso NEP141).
Mas ninguém apaga os +US$ 100 mil do livro-razão.
Repita isso 23 vezes. Então você vai ao balcão de saque e pede seu saldo em dinheiro, transferido para outro país (a ponte Omni). O cofre tem muito dinheiro de outros clientes, então o caixa paga você. Nada parece errado no balcão — o roubo já aconteceu na mesa de depósito.
O Que Aconteceu: Uma Análise Técnica Baseada na Atividade do GitHub
NEAR é assíncrono e baseado em recibos. Uma única ft_transfer_call não acontece em uma única etapa atômica, ela se divide em uma cadeia:
o contrato do token move os tokens -> chama ft_on_transfer no receptor -> a promise retornada do receptor é resolvida -> ft_resolve_transfer do contrato do token finaliza o resultado (reembolsos incluídos).

O NEAR explica isso na documentação do fluxo de transferência de tokens, e o guia de segurança FT alerta os desenvolvedores que os bugs vivem “na lacuna entre” essas etapas. Essa lacuna é onde este bug vivia.
O recurso depositnotification permitia que um depositante anexasse uma ação Notify a um depósito, fazendo com que o Verifier chamasse um contrato de terceiros arbitrário durante o fluxo de depósito. Aqui está o padrão vulnerável do código de prefixo, citado do diff do PR #374 (os comentários // <- são meus):
// 1. Internal ledger credit happens synchronously in ft_on_transfer:
self.deposit( // <- +$ written in the account book, right away
receiver_id.clone(),
[(token_id.clone(), amount.0)],
Some(”deposit”),
)
.unwrap_or_else(|err| err.panic());
// 2. ...then the async notification leg is kicked off:
DepositAction::Notify(notify) => Self::notify_on_transfer(
sender_id.clone(),
vec![sender_id],
receiver_id.clone(), // <- attackercontrolled target (the door that never opens)
vec![token_id.to_string()],
vec![amount],
notify,
)
.unwrap_or_else(|err| err.panic())
.then(
Self::ext(env::current_account_id())
.with_static_gas(Self::mt_resolve_deposit_gas(1))
.with_unused_gas_weight(0)
.ft_resolve_deposit(receiver_id, env::predecessor_account_id(), amount),
// <- supposed to keep the book consistent when things fail. it didn’t
)
.into(),O crédito (etapa 1) e o movimento definitivo do token (resolução da etapa 2, mais o próprio ft_resolve_transfer do contrato do token) vivem em diferentes etapas assíncronas.
Sob as regras NEP141, se a chamada do receptor falhar, a transferência é resolvida para um reembolso total do valor depositado. O caminho de rollback do Verifier, ft_resolve_deposit, deveria ter mantido o registro interno consistente com esse resultado. Não o fez.
Nas palavras do postmortem oficial:
“o atacante criou um contrato no Protocolo NEAR que estava falhando nessas chamadas de notificação, o que levou o contrato do token a devolver os fundos depositados, enquanto o contrato NEAR Intents mantinha o depósito registrado.”
Uma questão em aberto: o relatório oficial não diz exatamente por que o rollback falhou. O callback contabilizou incorretamente? Engoliu silenciosamente a falha? Ficou sem gas?
A atividade do GitHub nos dá dicas para duas delas:
• PR #367 (mesclado em 2 de outubro) alterou os erros de notificação para serem “relatados em vez de serem silenciosamente ignorados” -> aponta para o engolimento silencioso
• PR #374 aumentou cada constante de gas de resolvecallback -> sugere escassez de gas
Ambas são circunstanciais. Trate o submecanismo exato como uma reconstrução hipotética, não um fato confirmado. Estou apenas tentando entender toda a superfície de ataque, pois é um ótimo estudo de caso para a segurança do NEAR em geral.
O Vetor de Ataque
Agora vamos dar uma olhada no caminho completo do ataque, passo a passo:
1. Preparação: o atacante implanta um contrato no NEAR projetado para falhar (panic ou queimar o gas) sempre que for chamado como um receptor de depositnotification
2. Geração de fundos fantasma (23 vezes, ao longo de vários dias):
i. chama ft_transfer_call em um token NEP141, receptor intents.near, com uma DepositMessage contendo uma ação Notify apontando para o contrato malicioso
ii. o contrato do token transfere os tokens para intents.near e invoca ft_on_transfer
iii. o Verifier credita o saldo interno do atacante
iv. o Verifier chama o contrato do atacante -> a chamada falha
v. o ft_resolve_transfer do contrato do token reembolsa o depósito total ao atacante
vi. o registro interno mantém o crédito -> saldo fantasma, sem lastro
3. Efeito líquido: o registro diz Y + US$ 3,8 milhões, o contrato realmente detém Y, e o atacante possui US$ 3,8 milhões da diferença
4. A saída: de 29 de setembro, 16h14, a 1º de outubro, 05h54 (~37,7h), o atacante retirou os US$ 3,8 milhões através da ponte Omni para uma cadeia estrangeira. Como US$ 3,8 milhões < Y, os ativos reais do contrato cobriram isso e tudo foi executado normalmente
Nada nas transações de saída era inválido. O que significa que, no momento em que o atacante retirou, o roubo já havia sido feito, registrado no livro-razão como uma mentira. A saída apenas converteu a mentira em tokens reais em outra cadeia.
Quem foram as vítimas? Os usuários restantes dos tokens afetados, que agora estavam lastreando um contrato subcolateralizado sem saber. Eles foram ressarcidos antes mesmo da recuperação, aliás (do tesouro da equipe).
Post no X: https://x.com/i/status/2105642219357241796
Por Que Ninguém Notou Por Dias
NEAR Intents possui ferramentas e tarefas de monitoramento ativas. Neste caso, no entanto, não estava olhando para a superfície de ataque.
• monitorado -> tesourarias da cadeia de origem vs oferta do lado NEAR (X vs X). Essa é a ponte. O ataque nunca a tocou
• não monitorado -> saldos internos do usuário vs o que o contrato realmente detém (Y).
23 depósitos falhos. Vários dias. Zero alarmes.
As equipes geralmente monitoram as junções entre os sistemas porque é onde esperam problemas. Os invariantes que seu próprio registro cria muitas vezes não são observados. Esta é uma lição para todos que constroem neste espaço, não apenas para os desenvolvedores NEAR.
No entanto, uma vez que um humano percebeu, as coisas se moveram rapidamente (tempos conforme relatado no postmortem oficial):
• última retirada do atacante -> 1º de outubro, 05h54
• problemas de retirada identificados -> 08h04 (2h10m depois)
• intents.near pausado -> 08h33
• near.com em modo de manutenção -> 08h40
29 minutos da identificação à paralisação total. Isso é bem rápido.
As Correções: Principais Mudanças de Código Explicadas
Aqui está a linha do tempo da resposta, combinando o relatório oficial com o histórico do repositório:
- 1º de outubro, 11h20 / 14h00 -> contrato despausado (rotas afetadas mantidas pausadas), near.com restaurado
- 1º de outubro, noite -> PRs de endurecimento mesclados: #361, #363 (correção de alternância de autenticação), #364 (limites de token em lote). Lançado como v0.4.3 (#365)
- 2 de outubro -> #367 mesclado e lançado como v0.4.4 (#368): erros de notificação agora relatados em vez de silenciosamente ignorados
- 2 de outubro, 20h31 -> correção implantada na mainnet. Depósitos despausados às 20h38
- 2 de outubro -> todos os fundos devolvidos
- 7 de outubro -> um segundo bug crítico foi relatado no mesmo recurso através do programa de bug bounty (recompensa de US$ 300 mil para o pesquisador x.com/rohanganapa)
- 7 de outubro -> #374 remove as notificações de depósito inteiramente (v0.4.5)
Esta última parte é crítica e um excelente exemplo do ethos de trabalho por trás da equipe NEAR Intents. Após o hotfix, eles poderiam ter encerrado o dia. Em vez disso, quando um segundo relatório de recompensa atingiu o mesmo recurso cinco dias depois, eles decidiram eliminar o recurso de uma vez por todas:
pub enum DepositAction {
Execute(ExecuteIntents),
// Notify(NotifyOnTransfer), // <- gone (well, commented out. more on that below)
}O mesmo PR também aumentou cada constante de gas de resolvecallback em toda a linha:
| Constant | Before | After | File |
|||||
| `FT_RESOLVE_WITHDRAW_GAS` | 5 Tgas | 15 Tgas | `nep141/withdraw.rs` |
| `NFT_RESOLVE_WITHDRAW_GAS` | 5 Tgas | 15 Tgas | `nep171/withdraw.rs` |
| `MT_RESOLVE_TRANSFER_*` | 2/8 Tgas | 5/10 Tgas | `nep245/core.rs` |
| `MT_RESOLVE_WITHDRAW_*` | 2/8 Tgas | 5/10 Tgas | `nep245/withdraw.rs` |E a correção mais importante de todas: o monitoramento agora verifica o invariante de solvência (saldos internos totais vs saldo de token do contrato), com uma pausa automática do contrato se ele for violado.
Essa verificação sozinha teria parado este incidente em um depósito falho.
A equipe também anunciou a verificação formal como uma direção estratégica para os contratos do ecossistema NEAR e o bug bounty continua no HackenProof (até US$ 300 mil para bugs críticos). Notavelmente, ambos os relatórios críticos de 2026 sobre este recurso vieram através desse programa de recompensa, e nenhum foi explorado antes de ser relatado.
Minha Opinião Sobre Como a Equipe Lidou Com Isso
Não que eu esteja minimizando a falha na detecção. Longe disso. Monitorar o invariante errado por dias é uma falha real, e o próprio postmortem oficial admite que o recurso “tinha um histórico de relatórios de bugs, todos na lacuna entre suas duas etapas.”
Um relatório de bug bounty de janeiro sobre um recurso de chamada de terceiros semelhante foi corrigido na mainnet em dois dias (nunca explorado). Então veio o exploit de outubro. Depois o relatório de 7 de outubro. Três descobertas críticas em menos de um ano no mesmo recurso. A questão interessante, que o relatório oficial não responde, é por que o relatório de janeiro levou a um patch e não a uma remoção.
Mas sejamos justos.
Uma vez que um humano percebeu o exploit, o contrato foi pausado em 29 minutos, o produto estava parcialmente de volta no mesmo dia com as rotas afetadas mantidas congeladas, o hotfix foi lançado em menos de dois dias, e quando o recurso recebeu seu próximo relatório crítico, eles o removeram em vez de corrigi-lo novamente.
E então há a recuperação.
O atacante já havia completado a retirada total antes da detecção. Então não foram “fundos congelados no tempo”. Foi um trabalho de investigação e negociação que trouxe cada dólar de volta em aproximadamente 36 horas após a detecção. Um rollback da cadeia nunca esteve em questão (pelo menos não publicamente, que eu saiba).

Não perca a chance de estar com os principais líderes da indústria Blockchain! Uma experiência exclusiva com discussões profundas, networking de alto impacto e colaborações reais.
bitcoinblock.com.br
O caminho para a recuperação também foi puro cinema! Eu o cobri neste tópico aqui:
Post no X: https://x.com/i/status/2106000451585835448
Quão raro é isso? Muito. Não consigo me lembrar de muitos exploits em cripto que terminaram com 100% dos fundos devolvidos aos usuários. Geralmente é uma recuperação parcial, na melhor das hipóteses. E mais frequentemente do que eu gostaria, um rollback da cadeia é tratado como o caminho mais natural para a recuperação (o que prejudica a percepção da cadeia como uma rede de liquidação confiável, na minha opinião).
No geral: um bug sério, uma lacuna real de monitoramento e uma das melhores respostas a incidentes que já vi neste espaço. Trabalho excepcional.
Post no X: https://x.com/i/status/2105662433188565055
Observações Independentes
Lendo a atividade do GitHub lado a lado com o relatório oficial, algumas coisas se destacam que o relatório não aborda:
1. A correção comenta o código em vez de excluí-lo. O PR #374 comenta os caminhos Notify (e seus testes) em vez de removê-los. Incomum para uma remoção de segurança permanente — provavelmente escolhido pela velocidade e para facilitar a reversão. O risco: código morto que continua compilando contra tipos ativos pode ser silenciosamente revivido por uma refatoração futura. Até o momento, nenhuma limpeza de acompanhamento foi implementada.
2. Os aumentos de gas são evidências forenses. Cada constante de gas de resolvecallback aumentou no PR de remoção. Isso é consistente com callbacks rodando perto de seus limites de gas antes do incidente, o que apoia (mas não prova) a escassez de gas de ft_resolve_deposit como um fator contribuinte.
3. Erros estavam sendo silenciosamente engolidos. As próprias notas do PR #367 dizem que os erros de notificação foram “silenciosamente ignorados” antes de 2 de outubro. Um sistema que engole falhas de callback não pode manter um invariante de crédito/rollback. Para mim, esta é a leitura mais estrutural da causa raiz: o submecanismo exato importa menos do que o fato de que as falhas eram invisíveis.
4. O padrão foi documentado. O próprio guia de segurança do NEAR descreve este modo de falha exato (reembolsos inesperados, receptores falhos, contabilidade do resolvedor). O conhecimento estava lá, e o recurso foi lançado de qualquer forma e por uma equipe muito competente. Sempre há trabalho de segurança e “outra verificação” que vale a pena fazer. Alguém pode eventualmente encontrar o que todos nós perdemos.
5. A assimetria de monitoramento é a verdadeira lição. O par monitorado (cadeia de origem vs oferta NEAR) é um invariante de ponte. O par não monitorado (registro interno vs saldo do contrato) é a própria solvência do produto. As equipes de protocolo devem monitorar os invariantes que seu próprio registro introduz, não apenas as junções de integração.
Lista de Verificação de Segurança: Lições para Desenvolvedores NEAR
Se você constrói no NEAR (ou em qualquer runtime assíncrono baseado em recibos), é isso que deve tirar deste incidente.
Nunca credite um registro interno antes que o movimento do token seja final. Se o movimento definitivo for resolvido posteriormente em um callback assíncrono, seu caminho de rollback deve ser comprovadamente equivalente ao reembolso que o contrato do token executará.
Como verificar:
- Trate cada notificação entre contratos para um receptor arbitrário como entrada controlada pelo atacante — ela pode falhar, travar ou retornar valores hostis a qualquer momento na cadeia de promises (callbacks privados, sem panics em callbacks)
- Teste receptores falhos, valores de retorno maliciosos e casos de falta de gas
- Limite os reembolsos pelo valor original e saldo do receptor, mantenha os resolvedores privados
- Orçamente o gas do callback para que os caminhos de resolução não possam ser esgotados por receptores adversários
- Exponha os erros de callback. Nunca os engula silenciosamente
- Monitore seu invariante de solvência (passivos internos vs ativos on-chain) com uma pausa automática em caso de violação
- Quando um recurso continua a coletar relatórios críticos, talvez você deva removê-lo.
A lista de verificação de segurança do NEAR e o guia de segurança de implementação FT cobrem a maior parte disso. Estude-os cuidadosamente antes de escrever um fluxo de depósito.
Lembre-se: o submecanismo exato do rollback falho é minha reconstrução baseada na atividade do GitHub, não confirmada pelo postmortem oficial.
Fonte: Easily Explaining the NEAR Intents Exploit: $3.8M in Ghost Funds, 100% Recovered.
Isenção de responsabilidade: As opiniões, bem como todas as informações compartilhadas nesta análise de preços ou artigos mencionando projetos, são publicadas de boa-fé. Os leitores deverão fazer sua própria pesquisa e diligência. Qualquer ação tomada pelo leitor é prejudicial para sua conta e risco. O Bitcoin Block não será responsável por qualquer perda ou dano direto ou indireto.

Seu projeto cripto explicado por quem lê o código.
Vini B · thecoding. Pesquisa de segurança e escrita técnica em cripto e open source desde 2020, com bagagem em desenvolvimento blockchain e segurança de smart contracts. Conteúdo preciso, em português ou inglês, que traduz o complexo sem perder o rigor. A primeira conversa, de 15 a 20 minutos, é gratuita. Use o código BITCOIN BLOCK e receba 5% de desconto.





