Web3Exploit logoWeb3Exploit
← Back to Case Research

ZKsync $5M Case Research: Airdrop Admin Compromise and the sweepUnclaimed Mint

2025-04-15Operational Security4 min read

The ZKsync airdrop incident is smaller than Bybit or Cetus, but it teaches a sharp lesson: distribution contracts often keep privileged cleanup functions after the headline airdrop is done. If the admin key for those functions is compromised, "unclaimed allocation management" becomes unauthorized minting.

On April 15, 2025, an admin account tied to three ZKsync airdrop distribution contracts was compromised. The attacker called sweepUnclaimed() and minted roughly 111M ZK tokens, valued around $5M at the time. ZKsync reported that user funds and core protocol infrastructure were not affected, and later recovered funds through a bounty negotiation.

This write-up uses ZKsync/ZK Nation statements as reported by FullyCrypto and Blockonomi, plus public technical summaries. Code snippets are representative because the incident is primarily about privileged access to a distribution function, not a novel public exploit primitive.

The takeaway: a post-airdrop cleanup function is still a mint path if it can move unclaimed allocation. It deserves the same controls as treasury minting.

Target architecture: airdrop distribution contracts

Airdrop contracts usually track claims:

eligible user -> claim amount -> claimed or unclaimed

After a claim window, the protocol may want to recover unclaimed tokens. A function like sweepUnclaimed() is common:

function sweepUnclaimed(address to) external onlyAdmin {
    uint256 amount = unclaimedAllocation();
    _mintOrTransfer(to, amount);
}

The intended user story is legitimate: reclaim unused allocation for governance, treasury, or a future distribution. The dangerous property is that the function may still mint or transfer a very large amount in one call.

Root cause: compromised admin account

Public reporting states the admin account for the three airdrop distribution contracts was compromised. The compromised address was reported as:

0x842822c797049269A3c29464221995C56da5587D

With that authority, the attacker did not need to bypass onlyAdmin. They were the admin from the contract's perspective.

Representative vulnerable control plane:

contract AirdropDistributor {
    address public admin;
    IERC20Mintable public immutable ZK;
    mapping(address => bool) public claimed;

    function sweepUnclaimed(address receiver) external {
        require(msg.sender == admin, "not admin");

        uint256 amount = remainingUnclaimed();
        // Large privileged movement, no multisig/timelock/rate limit.
        ZK.mint(receiver, amount);
    }
}

The code might be internally correct under the assumption that admin is safe. The incident is the failure of that assumption.

Attack chain

  1. Compromise a distribution admin account. The exact off-chain method is not public.
  2. Call sweepUnclaimed() on the airdrop contracts. The attacker uses valid admin authority.
  3. Mint/receive 111M unclaimed ZK. Public reports place the supply impact around 0.45%.
  4. Swap part of the tokens for ETH. Blockonomi reports about $3.5M of stolen ZK was swapped for Ethereum.
  5. Team containment. ZKsync states the exploit was isolated to airdrop contracts and did not affect user funds or core protocol infrastructure.
  6. Bounty negotiation and return. ZK Nation's Security Council offered a 10% bounty for return of 90%. Blockonomi reports the attacker returned about $5.7M in assets after price movement.

Representative execution:

// From the chain's perspective this is an authorized admin call.
airdropDistributor.sweepUnclaimed(attacker);

// After receiving ZK, the attacker routes liquidity through DEXs.
router.swapExactTokensForETH(
    mintedZk,
    minEthOut,
    path,
    attacker,
    block.timestamp
);

The important forensic distinction: this is not a proof bypass or signature malleability bug. It is valid privileged execution by an account that should not have been under attacker control.

Why the function was dangerous

Cleanup functions often get less scrutiny than user-facing claim paths. That is backwards. A claim path moves a bounded amount per user. A sweep path can move everything that remains.

The better model:

function queueSweep(address receiver, uint256 amount) external onlyMultisig {
    require(amount <= maxSweepAmount, "too large");
    queuedSweep = Sweep(receiver, amount, block.timestamp + TIMELOCK);
}

function executeSweep() external {
    require(block.timestamp >= queuedSweep.executeAfter, "timelocked");
    ZK.transfer(queuedSweep.receiver, queuedSweep.amount);
}

That does not make key compromise impossible. It turns immediate mint-and-dump into a monitored, cancellable action.

Transaction trail

Public reports identify:

  • Incident date: April 15, 2025
  • Compromised admin address: 0x842822c797049269A3c29464221995C56da5587D
  • Minted amount: 111M ZK
  • Initial value: about $5M
  • Recovery: about $5.7M returned after a 10% bounty negotiation
  • ZKsync X update quoted by FullyCrypto: https://twitter.com/zksync/status/1912165357642473488
  • ZK Nation recovery update quoted by Blockonomi: https://twitter.com/TheZKNation/status/1915110305790660939

What I would change

  1. Treat airdrop sweep as treasury minting. It needs multisig/MPC, timelock, event monitoring, and explicit limits.
  2. Remove or permanently disable mint paths after use. If unclaimed tokens can be pre-funded instead of minted, prefer transfer over mint.
  3. Rate-limit high-supply movements. A single call should not mint or transfer a large supply percentage without delay.
  4. Separate operational and emergency authority. The keys that can sweep should not be the only keys that can pause or revoke.
  5. Publish distribution-contract inventories. Every live contract retaining privileged functions should be known and monitored.

Confidence notes

  • Firm: public ZKsync-linked reporting says the admin account for the airdrop distribution contracts was compromised.
  • Firm: the attacker called sweepUnclaimed() and minted roughly 111M ZK.
  • Firm: core protocol infrastructure and user funds were reported unaffected.
  • Firm: a bounty negotiation led to return of funds reported around $5.7M.
  • Unclear: the initial method of admin account compromise has not been fully disclosed.

Sources

  • FullyCrypto — ZKsync Suffers $5M Hack Through Compromised Admin Account: https://fullycrypto.com/zksync-suffers-5-million-hack-through-compromised-admin-account
  • Blockonomi — ZKsync Recovers $5.7M After Bounty Negotiation: https://blockonomi.com/zksync-recovers-5-7-million-in-stolen-crypto-after-bounty-negotiation/
  • ZKsync update quoted by FullyCrypto: https://twitter.com/zksync/status/1912165357642473488
  • ZK Nation recovery update quoted by Blockonomi: https://twitter.com/TheZKNation/status/1915110305790660939