Durante a última semana (19/10/2026–25/10/2026), este relatório acompanhou dois incidentes de segurança com perdas estimadas em aproximadamente 5,57 milhões USD. A análise detalhada se concentra na tomada de controle da governança do X100 Freezer na Ethereum.
| Data | Incidente | Tipo | Perda estimada* |
|---|---|---|---|
| 21/10/2026 | NEM Token | Falha na lógica de negócios | ~820 mil USD |
| 24/10/2026 | X100 Freezer | Falha no desenho da governança | ~4,75 milhões USD |
*As perdas são aproximadas. O valor do X100 Freezer cobre a transação representativa analisada abaixo e corresponde à estimativa inicial da Phalcon; o total não inclui extrações posteriores relatadas separadamente. A data do NEM é a data da transação on-chain; o alerta público foi publicado em 22 de outubro [1].
- X100 Freezer: O incidente mostra o risco combinado da elegibilidade para a governança com efeito imediato e da ausência de um timelock para a execução: o atacante usou fundos obtidos por empréstimo-relâmpago para comprar e congelar tokens, conquistou imediatamente a maioria das cadeiras no Senado e aprovou e executou uma proposta maliciosa para o tesouro na mesma transação.
O melhor auditor de segurança para Web3
Valide o desenho, o código e a lógica de negócios antes do lançamento
Destaque da semana: X100 Freezer
Em 24/10/2026, o X100 Freezer, um sistema on-chain de staking e gestão de liquidez na Ethereum, foi explorado, com perdas de aproximadamente 4,75 milhões USD na transação examinada aqui. Uma falha no desenho da governança permitia que saldos congelados recém-adquiridos conferissem cadeiras no Senado imediatamente, sem um timelock entre a aprovação e a execução de uma proposta. O atacante garantiu duas das três cadeiras, direcionou uma troca de ativos do tesouro para um pool sob seu controle e vendeu parte dos tokens recuperados para pagar o empréstimo-relâmpago e obter lucro [2].
Contexto
Os usuários do X100 congelam tokens X em posições de staking divididas por níveis para receber recompensas. O contrato Freezer em 0x79f3...2a46 também gerencia a liquidez do projeto na Uniswap V3/V4 e na PancakeSwap V3. Por meio de DexTreasury, ele herda o contrato de governança Senate; o Senado decide as operações do tesouro por meio de propostas e votos.
O ponto de entrada público freeze() transfere os tokens do usuário para o contrato e, em seguida, chama a função privada _freeze() para criar uma posição de staking e atualizar sua contabilização. _tierTotal[owner][tier] registra a quantidade congelada de cada conta em cada nível. _freeze() atualiza esses saldos e notifica o sistema de governança por meio de _onStakeChanged(owner):
function _freeze(address owner, uint8 tier, uint256 amount) private returns (uint256 id) {
_settle(owner);
Account storage account = _accounts[owner];
if (account.firstFreezeAt == 0) {
account.firstFreezeAt = block.timestamp;
}
id = _create(owner, tier, amount, block.timestamp, block.timestamp);
_tierTotal[owner][tier] += amount;
totalFrozen += amount;
totalFrozenIn[tier] += amount;
_onStakeChanged(owner);
emit Frozen(id, owner, tier, amount);
}
Para entrar na lista de candidatos ao Senado, uma conta chama applyForSenate(). Isso invoca _register(), que registra a candidatura, cobra a taxa de candidatura e chama o mesmo gancho de atualização do stake:
function _register(address account) internal {
require(!_registered[account], AlreadyRegistered(account));
_registered[account] = true;
_collectCandidacyFee(account);
emit Registered(account);
_onStakeChanged(account);
}
O gancho ignora contas não registradas. Para as contas registradas, ele mantém uma lista classificada pelos saldos congelados, inserindo ou movendo contas por meio de _link(). isSenator() identifica como senadores as primeiras senateSize contas dessa lista. O sistema comporta de 3 a 21 cadeiras, cada uma com direito a um voto. Os tokens congelados estão sujeitos a um prazo para saque, calculado por _unfreezeAt() a partir do timestamp frozenAt da posição e do nível de staking.
Um senador apresenta uma operação de tesouro permitida por meio de propose(), que registra a proposta e atribui automaticamente um voto favorável do proponente. Os demais senadores votam por meio de vote(), e _tally() conta os votos favoráveis entre os ocupantes atuais das cadeiras. A proposta registra seu horário de criação e um prazo final determinado por votingPeriod, cujo padrão é de três dias.
Funções do tesouro como v3Swap() são alvos de proposta permitidos em DexTreasury._isProposable(). Seu modificador onlySenate exige que a chamada venha do próprio contrato. O fluxo de execução faz essa chamada a si mesmo usando os dados da proposta aprovada. Depois, Freezer._afterExecute() verifica se o saldo restante de X cobre totalFrozen, preservando o principal congelado agregado.
Análise da vulnerabilidade
A falha no desenho da governança do Freezer consistia na ausência de um período de espera antes que o stake recém-adquirido pudesse conferir autoridade no Senado e de um timelock antes da execução de uma proposta aprovada.
Primeiro, o stake usado para a classificação vem diretamente dos saldos congelados atuais. _stakeOf() chama frozenOf(), que soma _tierTotal sem verificar a antiguidade do stake nem consultar um saldo histórico:
function _stakeOf(address account) internal view override returns (uint256) {
return frozenOf(account);
}
function frozenOf(address owner) public view returns (uint256 total) {
total = 0;
for (uint8 tier = 0; tier < TIERS; ++tier) {
total += _tierTotal[owner][tier];
}
}
Os fluxos de depósito e registro mostrados acima atualizam a elegibilidade na mesma sequência de chamadas. Embora _freeze() registre timestamps para a posição, as verificações de classificação e de condição de senador não os utilizam para impor um período mínimo de manutenção. Assim, um novo candidato com fundos suficientes pode obter uma cadeira e exercer seus direitos de apresentar propostas e votar na mesma transação. O prazo para saque dos tokens não adia esses direitos de governança.
Segundo, o período de votação de três dias determina apenas quando uma proposta expira. _state() retorna Open assim que a proposta passa a existir, e ela permanece aberta até a execução, o cancelamento ou a expiração. A verificação da maioria independe do tempo decorrido:
function _state(Proposal storage proposal) private view returns (ProposalState) {
if (proposal.executed) {
return ProposalState.Executed;
}
if (proposal.cancelled) {
return ProposalState.Cancelled;
}
if (block.timestamp > proposal.deadline) {
return ProposalState.Expired;
}
return ProposalState.Open;
}
function _isPassed(uint256 support) private view returns (bool) {
return support * 2 > senateSize;
}
execute() aceita uma proposta aberta assim que os votos favoráveis excedem a metade de senateSize. Antes de chamar a operação do tesouro, ele não verifica nem um intervalo mínimo desde createdAt nem um período de espera após a aprovação:
function execute(uint256 id) external {
Proposal storage proposal = _requireOpen(id);
uint256 size = senateSize;
(uint256 support, address[] memory seats) = _tally(id);
require(_isPassed(support), NotPassed(id, support, size));
proposal.executed = true;
(bool ok, bytes memory result) = address(this).call(proposal.data);
if (!ok) {
assembly ("memory-safe") {
revert(add(result, 32), mload(result))
}
}
_afterExecute(id);
emit ProposalExecuted(id, _msgSender(), support, size, seats);
}
A chamada a si mesmo satisfaz onlySenate, portanto a verificação de acesso ao tesouro não acrescenta nenhum atraso à execução. A verificação final do principal congelado não restringe quando a proposta é executada nem impõe um retorno mínimo para a troca de ativos do tesouro. Essas condições permitem que uma maioria recém-formada autorize e execute uma operação de tesouro em uma única transação.
Análise do ataque
A análise a seguir se baseia na transação 0x38c608...4d81d0.
-
Etapa 1: O atacante usou fundos obtidos por empréstimo-relâmpago para comprar tokens
Xe os distribuiu entre dois contratos sob seu controle. Os fundos emprestados financiaram a compra dos tokens que estabeleceriam as duas posições de staking. -
Etapa 2: Cada contrato sob controle do atacante congelou seus tokens
Xno Freezer e chamouapplyForSenate(). O rastreamento mostra que ambos os contratos receberam um eventoRankedapós o registro. Seus saldos congelados garantiram duas cadeiras no Senado dentro da transação. No momento do ataque, o Senado tinha três cadeiras, portanto o controle de duas delas proporcionava a maioria necessária para aprovar uma proposta de tesouro.

- Etapa 3: O atacante criou um pool
X/USDTna PancakeSwap V3 e forneceu sua liquidez. Um dos contratos sob seu controle apresentou uma proposta de troca de ativos do tesouro, registrando automaticamente um voto favorável; o outro registrou o segundo voto favorável. Com o limiar de maioria atingido, o Freezer executou a proposta 7. A chamada av3Swap()especificava 400.000Xcomo entrada eamountOutMinimum = 0. O tesouro recebeu apenas cerca de 35,68USDT. O rastreamento registravotesFor = 2esenateSize = 3, mostrando que a maioria conquistada autorizou a operação.

- Etapa 4: O atacante retirou a liquidez do pool que acabara de criar na PancakeSwap V3 para recuperar os tokens
Xdo tesouro. Vendeu parte desses tokens por meio do poolX/USDTjá existente na Uniswap V3 e pagou o empréstimo-relâmpago. Os recursos para o pagamento vieram da venda subsequente, e não de um saque imediato das posições de staking congeladas.
Comece a usar o Phalcon Explorer
Explore as transações para agir com conhecimento
Experimente gratuitamente agoraConclusão
O incidente resultou de uma falha no desenho da governança: os saldos congelados atuais conferiam autoridade imediatamente, e propostas aprovadas podiam ser executadas sem um timelock. O atacante combinou essas condições para conquistar uma maioria e usá-la antes do vencimento do empréstimo-relâmpago. O tesouro então vendeu 400.000 X por cerca de 35,68 USDT por meio do pool sob controle do atacante na PancakeSwap V3. O atacante retirou a liquidez desse pool para recuperar os tokens do tesouro, vendeu parte deles por meio do pool X/USDT separado e já existente na Uniswap V3 e usou os recursos obtidos para pagar o empréstimo-relâmpago e ficar com um lucro.
A governança deve exigir um tempo mínimo de permanência em staking ou determinar a elegibilidade para as cadeiras e os direitos de voto a partir de um snapshot histórico dos saldos, obtido em um bloco anterior à transação atual. Isso impede que posições financiadas dentro da transação autorizem imediatamente movimentações do tesouro. Um timelock entre a aprovação e a execução deve impedir que toda a sequência de tomada de controle e extração seja concluída em uma única transação e dar tempo para analisar ou cancelar uma proposta maliciosa. Esses controles abordam etapas distintas do fluxo de governança e devem ser avaliados em conjunto durante uma auditoria de contratos inteligentes.
Referências
[1] Phalcon da BlockSec: análise do incidente com o NEM Token
[2] Phalcon da BlockSec: análise do incidente de governança do X100 Freezer
Verificação de segurança gratuita
Uma análise rápida de segurança com nosso mecanismo interno de análise automatizada.
Faça uma verificação gratuitaSobre a BlockSec
A BlockSec é uma fornecedora completa de segurança blockchain e conformidade cripto. Desenvolvemos produtos e serviços que ajudam clientes a realizar auditorias de código (incluindo contratos inteligentes, blockchains e carteiras), interceptar ataques em tempo real, analisar incidentes, rastrear fundos ilícitos e cumprir obrigações de AML/CFT ao longo de todo o ciclo de vida de protocolos e plataformas.
A BlockSec publicou diversos artigos sobre segurança blockchain em conferências de prestígio, relatou vários ataques de dia zero contra aplicações DeFi, bloqueou diversas invasões para resgatar mais de 20 milhões USD e protegeu bilhões em criptomoedas.
-
Site oficial: https://blocksec.com/
-
Conta oficial no Twitter: https://twitter.com/BlockSecTeam



