Back to Blog

~$5.57M Lost: X100 Freezer, NEM Exploits | BlockSec Weekly

Code Auditing
October 29, 2026
9 min read
Key Insights
  • This report covers two incidents on BNB Chain and Ethereum, with approximately $5.57M in estimated losses under the scope noted below.

  • NEM Token suffered from flawed business logic in tax settlement. X100 Freezer's flawed governance design allowed a flash-loan-funded takeover of its Senate.

  • X100 Freezer assigned governance authority from current frozen balances and allowed immediate proposal execution, leaving treasury decisions exposed to newly acquired voting power.

During the past week (2026/10/19 - 2026/10/25), this report tracked two security incidents with approximately $5.57M in estimated losses. The detailed analysis focuses on X100 Freezer's governance takeover on Ethereum.

Date Incident Type Estimated Loss*
2026/10/21 NEM Token Flawed Business Logic ~$820K
2026/10/24 X100 Freezer Flawed Governance Design ~$4.75M

*Losses are approximate. X100 Freezer's figure covers the representative transaction analyzed below, matching Phalcon's initial estimate; the total does not include separately reported follow-on extractions. NEM's date is the on-chain transaction date; the public alert was posted on October 22 [1].

  • X100 Freezer: The incident shows the combined risk of immediately effective governance eligibility and the absence of an execution timelock: the attacker used flash-loaned funds to purchase and freeze tokens, immediately acquired a majority of Senate seats, and passed and executed a malicious treasury proposal within the same transaction.

Best Security Auditor for Web3

Validate design, code, and business logic before launch

Weekly Highlight: X100 Freezer

On October 24, 2026, X100 Freezer, an on-chain staking and liquidity management system on Ethereum, was exploited for approximately $4.75M in the transaction examined here. A flawed governance design allowed newly acquired frozen balances to confer Senate seats immediately, with no timelock separating proposal approval from execution. The attacker secured two of three seats, directed a treasury swap into an attacker-controlled pool, and sold part of the recovered tokens to repay the flash loan and realize a profit [2].

Background

X100 users freeze X tokens in tiered staking positions to earn rewards. The Freezer contract at 0x79f3...2a46 also manages the project's liquidity on Uniswap V3/V4 and PancakeSwap V3. It inherits the Senate governance contract through DexTreasury; the Senate decides treasury operations through proposals and votes.

The public freeze() entry point transfers the user's tokens into the contract, then calls the private _freeze() function to create a staking position and update its accounting. _tierTotal[owner][tier] records each account's frozen amount in each tier. _freeze() updates these balances and notifies the governance system through _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);
}

To join the Senate candidate list, an account calls applyForSenate(). This invokes _register(), which records the registration, collects the candidacy fee, and calls the same stake-update hook:

function _register(address account) internal {
    require(!_registered[account], AlreadyRegistered(account));

    _registered[account] = true;
    _collectCandidacyFee(account);

    emit Registered(account);

    _onStakeChanged(account);
}

The hook skips unregistered accounts. For registered accounts, it maintains a list ranked by frozen balances, inserting or moving accounts through _link(). isSenator() identifies the first senateSize accounts in that list as senators. The system supports 3 to 21 seats, with each seat carrying one vote. Frozen tokens are subject to a withdrawal term, calculated by _unfreezeAt() from the position's frozenAt timestamp and staking tier.

A senator submits an allowed treasury operation through propose(), which records the proposal and automatically casts the proposer's supporting vote. Other senators cast their votes through vote(), and _tally() counts supporting votes among the current seats. The proposal records its creation time and a deadline determined by votingPeriod, which defaults to three days.

Treasury functions such as v3Swap() are allowed proposal targets in DexTreasury._isProposable(). Their onlySenate modifier requires a call from the contract itself. The execution path makes this self-call using the approved proposal data. Afterward, Freezer._afterExecute() checks that the remaining X balance covers totalFrozen, preserving the aggregate frozen principal.

Vulnerability Analysis

The Freezer's flawed governance design lacked a delay before newly acquired stake could confer Senate authority and a timelock before an approved proposal could execute.

First, the stake used for ranking comes directly from current frozen balances. _stakeOf() calls frozenOf(), which sums _tierTotal without checking stake age or reading a historical balance:

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];
    }
}

The deposit and registration paths shown above update eligibility within the same call sequence. Although _freeze() records timestamps for the position, the ranking and senator checks do not use them to enforce a minimum holding period. A sufficiently funded new candidate can therefore obtain a seat and exercise its proposal and voting rights within the same transaction. The token withdrawal term does not delay these governance rights.

Second, the three-day voting period only determines when a proposal expires. _state() returns Open as soon as the proposal exists and remains open until execution, cancellation, or expiry. The majority check is independent of elapsed time:

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() accepts an open proposal as soon as its supporting votes exceed half of senateSize. It checks neither a minimum interval since createdAt nor a delay after approval before calling the treasury operation:

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);
}

The self-call satisfies onlySenate, so the treasury access check adds no execution delay. The final frozen-principal check does not constrain when the proposal executes or enforce the treasury swap's minimum return. These conditions allow a newly established majority to authorize and execute a treasury operation within one transaction.

Attack Analysis

The following analysis is based on the transaction 0x38c608...4d81d0.

  • Step 1: The attacker used flash-loaned funds to purchase X tokens and distributed them to two controlled contracts. The borrowed funds financed the token purchase that would establish the two staking positions.

  • Step 2: Each controlled contract froze its X in the Freezer and called applyForSenate(). The trace shows both contracts receiving a Ranked event after registering. Their frozen balances secured two Senate seats within the transaction. At the time of the attack, the Senate had three seats, so controlling two provided the majority needed to pass a treasury proposal.

  • Step 3: The attacker created a PancakeSwap V3 X/USDT pool and supplied its liquidity. One controlled contract proposed a treasury swap, automatically casting a supporting vote; the other cast the second supporting vote. With the majority threshold met, the Freezer executed proposal 7. Its v3Swap() call specified 400,000 X as input and amountOutMinimum = 0. The treasury received only about 35.68 USDT. The trace records votesFor = 2 and senateSize = 3, showing that the captured majority authorized the operation.
  • Step 4: The attacker withdrew liquidity from their newly created PancakeSwap V3 pool to recover the treasury's X. They sold part of those tokens through the existing Uniswap V3 X/USDT pool and repaid the flash loan. The source of repayment was the subsequent sale, rather than an immediate withdrawal of the frozen staking positions.

Get Started with Phalcon Explorer

Dive into Transactions to Act Wisely

Try now for free

Conclusion

The incident resulted from flawed governance design: current frozen balances immediately conferred authority, and approved proposals could execute without a timelock. The attacker combined these conditions to acquire a majority and use it before the flash loan had to be repaid. The treasury then sold 400,000 X for about 35.68 USDT through the attacker-controlled PancakeSwap V3 pool. The attacker withdrew liquidity from that pool to recover the treasury's tokens, sold part of them through the separate, existing Uniswap V3 X/USDT pool, and used the proceeds to repay the flash loan and retain a profit.

Governance should require a minimum staking age or determine seat eligibility and voting rights from a historical balance snapshot taken in a block before the current transaction. This prevents positions funded within the transaction from immediately authorizing treasury movements. A timelock between approval and execution should prevent the complete takeover-and-extraction sequence from settling within one transaction and provide time to review or cancel a malicious proposal. These controls address separate stages of the governance path and should be assessed together during a smart contract audit.

References

[1] BlockSec Phalcon: NEM Token incident analysis

[2] BlockSec Phalcon: X100 Freezer governance incident analysis

Free Security Scan

A quick security pass with our in-house automated analysis engine.

Scan for free

About BlockSec

BlockSec is a full-stack blockchain security and crypto compliance provider. We build products and services that help customers to perform code audit (including smart contracts, blockchain and wallets), intercept attacks in real time, analyze incidents, trace illicit funds, and meet AML/CFT obligations, across the full lifecycle of protocols and platforms.

BlockSec has published multiple blockchain security papers in prestigious conferences, reported several zero-day attacks of DeFi applications, blocked multiple hacks to rescue more than 20 million dollars, and secured billions of cryptocurrencies.

Sign up for the latest updates
~$3.87M Lost: Autheo Network, UNCX Hacks | BlockSec Weekly
Security Insights

~$3.87M Lost: Autheo Network, UNCX Hacks | BlockSec Weekly

This weekly report covers 3 security incidents from October 12 to October 18, 2026, with approximately $3.87M in total estimated losses. Autheo Network suffered a ~$1.76M private key compromise, Qubic Wallet lost ~$1.30M due to weak seed generation, and UNCX Network's Smart Token Vesting V2 contracts suffered an ~$807K accounting error across Ethereum, BNB Chain, Base, and Robinhood Chain. The highlighted UNCX analysis explains how a flawed aggregate share calculation mixed vesting tranches created at different token-to-share ratios and enabled excess withdrawals from shared pools.

~$542K Lost: MakerDaoBot Exploit | BlockSec Weekly
Security Insights

~$542K Lost: MakerDaoBot Exploit | BlockSec Weekly

This report covers 1 security incident on Ethereum from 2026/10/05 - 2026/10/11, with an estimated loss of approximately $542K. An attack contract deployed a malicious adapter and passed it to a MakerDaoBot function that lacked access control, causing the bot to grant the adapter permission over its internal MakerDAO balances. The adapter settled four legacy ETH-A auctions and converted 200 units of ETH-A collateral to WETH; the attack contract unwrapped it and sent 200 ETH to the attacker.

~$418M Lost: Bitget, NEAR Intents Exploits | BlockSec Weekly
Security Insights

~$418M Lost: Bitget, NEAR Intents Exploits | BlockSec Weekly

This biweekly security report records eight incidents from September 21 to October 4, 2026, with approximately $418.4M in losses across Ethereum, BNB Chain, Solana, Bitcoin, TRON, XRP Ledger, Zcash, Avalanche, NEAR, and related networks. Detailed analysis covers Bitget's $387.5M third-party security product vulnerability and NEAR Intents' $3.87M refund validation flaw and missing state rollback.

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit