Back to Blog

Weekly Web3 Security Incident Roundup | Mar 9 – Mar 15, 2026

March 18, 2026
24 min read

During the past week (2026/03/09 - 2026/03/15), BlockSec detected and analyzed eight attack incidents, with total estimated losses of ~$1.66M. The table below summarizes these incidents, and detailed analyses for each case are provided in the following subsections.

Date Incident Type Estimated Loss
2026/03/09 EtherFreakers Incident Flawed business logic ~$25K
2026/03/10 Alkemi Incident Flawed business logic ~$89K
2026/03/10 MT Incident Flawed business logic ~$242K
2026/03/11 AAVE Liquidation Incident Misconfiguration ~$1.01M
2026/03/11 Planet Finance Incident Flawed business logic ~$10K
2026/03/12 AM Incident Flawed business logic ~$131K
2026/03/12 DBXen Incident Flawed business logic ~$149K
2026/03/15 Goose Finance Incident Flawed business logic ~$8K

EtherFreakers Incident

Brief Summary

On March 9, 2026, EtherFreakers, an NFT game on Ethereum, was exploited due to incorrect double counting, resulting in a loss of ~$25K. Each NFT in the game holds a withdrawable ETH balance (called "energy"). As a game mechanic, players can use attack() to have one NFT capture another and claim the target's energy. However, the contract pays out the target's balance but transfers the NFT before settling its accounting. A transfer hook then reads stale, pre-payout data and feeds part of it back into a global dividend pool, inflating the pool without new ETH backing. The exploiter looped this capture mechanic to pump the global index and then drained the inflated balance from a batch of NFTs.

Background

EtherFreakers is an on-chain NFT game where each NFT (called a "Freaker") holds a withdrawable ETH balance called energy. The system works like a dividend pool: when certain actions occur, a fraction of ETH is distributed across all Freakers proportionally. Each Freaker's claimable ETH is tracked by a global accumulator freakerIndex combined with a per-token share weight fortune.

Concretely, the accounting formula is: energyOf = basic + (freakerIndex - index) * fortune. The freakerIndex increases when _dissipateEnergyIntoPool(amount) runs, distributing 80% of amount to all Freakers and 20% to creators. Direct deposits via charge() only increase basic without affecting freakerIndex. Therefore, increases in freakerIndex should always be backed by real Ether entering the system. If freakerIndex grows without corresponding ETH inflow, Freakers can redeem more Ether than the contract actually holds.

Vulnerability Analysis

The root cause is an incorrect execution order in the EtherFreak contract (0x3A27...c0f33). When a capture succeeds, the attack() function executes these steps in order:

  1. Line 237: Pay targetCharge (the target NFT's full energy) to the defender as a direct ETH transfer. The energy is now spent.
  2. Line 240: Call _transfer(defender, capturer, targetId) to move the NFT. Internally, _transfer() invokes the ERC-721 hook _beforeTokenTransfer(), which calls _dissipateEnergyIntoPool() with 0.1% of energyOf(targetId). This is the first call to _dissipateEnergyIntoPool(), and it reads a stale value because step 5 has not happened yet.
  3. Line 241: Call _dissipateEnergyIntoPool(sourceSpent) explicitly. This is the second call, which is part of normal game logic.
  4. Lines 244-251: Update energyBalances for both sourceId and targetId.

The bug is in step 2: because energyBalances[targetId] has not been updated yet, the hook still sees the pre-payout balance and feeds part of the already-spent energy into the dividend pool. The direct ETH payout in step 1 and the pool input in step 2 both draw from the same energy, inflating freakerIndex without new ETH backing.

freakerIndex grows whenever _dissipateEnergyIntoPool() is called:

Attack Analysis

The following analysis is based on the transaction 0x89e24d...9abd2942.

  • Step 1: Borrow 1,700 WETH.

  • Step 2: Mint two fresh Freakers, token 590 and token 591, under exploiter-controlled addresses.

  • Step 3: Repeatedly call the game's attack(590, 591) function and keep executions on the successful capture branch.

  • Step 4: After each success, transfer token 591 back to the helper so the same pair can be reused.

  • Step 5: Each successful loop inflates freakerIndex beyond the Ether actually conserved by the system.

  • Step 6: Once the index is high enough, discharge a batch of previously controlled Freakers. Token IDs 496 through 520 are each discharged for 0.278052246002402082 Ether.

  • Step 7: Wrap the drained Ether into WETH, repay the 1,700 WETH flash loan, and keep about 7.498 WETH as profit.

Conclusion

The root cause is in the successful capture flow of attack(): EtherFreakers pays out targetCharge before the target token's energy state is settled. _transfer() then triggers _beforeTokenTransfer(), which reads stale pre-payout energyOf(targetId) and dissipates part of it into the pool. This increases freakerIndex without new Ether backing, so the same target energy is counted both as payout and as pool input. This is a business-logic inflation bug, not a reentrancy bug.

To reduce similar risks in the future:

  • Avoid recomputing economic values from mutable state inside transfer hooks while the same transaction is still settling.

  • If the transfer hook reads a state variable, ensure the order of execution does not affect the result (e.g., settle the state before the hook runs, not after).


Alkemi Incident

Brief Summary

On March 10, 2026, the Alkemi protocol on Ethereum was exploited, resulting in a loss of ~$89K. The root cause is an accounting error and flawed business logic. The flawed liquidation logic allows anyone to liquidate their own position within the same transaction and profit from it. In addition, an accounting error causes the deduction of the attacker's own collateral during liquidation to be overwritten, allowing the attacker to obtain liquidation rewards without bearing the intended cost.

Background

Alkemi is a lending protocol. When a borrower's position becomes undercollateralized, anyone can call liquidateBorrow() to repay part of the debt and seize collateral at a discount. To prevent excessive liquidation, the protocol caps the repayable amount per transaction at the minimum of three values:

  1. The borrower's current borrow balance (currentBorrowBalance_TargetUnderwaterAsset).
  2. The maximum repayment the borrower's collateral can cover after applying the liquidation discount (calculateDiscountedBorrowDenominatedCollateral()).
  3. The repayment amount needed to bring the account back to the liquidation boundary (calculateDiscountedRepayToEvenAmount()), checked only when the market isSupported.

Vulnerability Analysis

The root cause is flawed business logic and an accounting error in the Alkemi protocol (0x4822...a888). Since currentBorrowBalance_TargetUnderwaterAsset is necessarily greater than 0 as long as the borrower has an outstanding debt, and the value returned by calculateDiscountedBorrowDenominatedCollateral() is also necessarily greater than 0 as long as the borrower has collateral, the AlkemiEarnPublic protocol effectively relies on calculateDiscountedRepayToEvenAmount() to determine whether a given loan can be liquidated. In this function, the amount of debt that needs to be liquidated should be calculated based on a variable called accountShortfall_TargetUser.

However, in the actual implementation, the function instead uses a global variable closeFactorMantissa to calculate an upper limit on the amount of debt that is allowed to be repaid, and returns this value. As a result, an attacker is able to borrow and immediately liquidate their own position within the same transaction.

In addition, in the function liquidateBorrow(), when the liquidator and the borrower are the same address, the variables supplyBalance_TargetCollateralAsset and supplyBalance_LiquidatorCollateralAsset point to the same storage slot. The function then calculates a "reduced balance" and a "rewarded balance" separately based on the same initial balance, and subsequently writes them back to the same storage slot in sequence. Because the reduced balance is written first and the rewarded balance is written afterward, the reduction effect is overwritten and lost, leaving only the rewarded result. This allows the attacker to further amplify their profit.

Attack Analysis

The following analysis is based on the transaction 0xa170...6d9d.

  • Step 1: The attacker takes a flash loan of 51e18 WETH from Balancer.

  • Step 2: The attacker unwraps 51e18 WETH into ETH and supplies it to the Alkemi protocol.

  • Step 3: The attacker borrows 39.5e18 ETH from Alkemi.

  • Step 4: The attacker liquidates their own position using 39.5395e18 ETH.

  • Step 5: The attacker withdraws 93.5e18 ETH from Alkemi.

  • Step 6: The attacker repays the flash loan and makes a profit of 43.4e18 ETH.

Conclusion

The root cause is that the flawed liquidation logic allows the attacker to borrow and then liquidate their own position within the same transaction to make a profit, while the incorrect accounting logic further amplifies the attacker's gains.

To reduce similar risks in the future:

  • For balance updates to the borrower and the liquidator, the protocol should operate directly on storage variables rather than copying the balances into temporary memory variables for separate calculation and write-back.

MT Incident

Brief Summary

On March 10, 2026, MT Token, a deflationary token on BNB Chain, was exploited, resulting in a loss of ~$242K. The root cause is flawed trading restriction logic combined with inconsistent handling of special transfer conditions. During the deflation phase, the contract restricts buy operations when the pool reserve exceeds a fixed threshold. However, the contract treats transfers of exact amounts (e.g., 2e17 MT) as referral-binding actions, allowing the attacker to bypass the buy restriction and acquire initial tokens. In addition, the restriction logic relies on incomplete path detection (isBuy) and fails to cover indirect swap routes such as Pair to Router, while whitelist checks further short-circuit critical validations. The attacker accumulated MT tokens without triggering restrictions or fees, manipulated pendingBurnAmount via controlled liquidity operations and trades, and forced the pool into an abnormal state where the token price was artificially inflated.

Background

MT Token is a deflationary token on BNB Chain with built-in trading restrictions. During the deflation phase, the contract blocks buy operations when the MT reserve in the pool exceeds 21,000e18. Once the reserve falls below this threshold, the deflation phase ends and buying is re-enabled. MT Token also includes a referral mechanism: a transfer of exactly 2e17 MT or 1e17 MT is treated as a referrer-binding action rather than a normal trade.

Vulnerability Analysis

The root cause was a flawed buy-restriction design in the MT contract (0x037E...b449). Under normal conditions, attackers should be unable to acquire MT as seed capital during the restricted phase. However, the contract treats a transfer of exactly 2e17 MT as a referral-binding action rather than a buy, which allows an attacker to purchase 2e17 MT while bypassing the buy restriction.

Additionally, the trading restriction relies on the isBuy branch to block purchases, but it does not cover the "Pair to Router" path. Since both the Router and Pair are whitelist addresses, such transfers short-circuit at the whitelist check and never reach the buy restriction logic, allowing an attacker to acquire MT by routing buys to the Router and subsequently extracting tokens back to themselves by removing liquidity.

Attack Analysis

The following analysis is based on the transaction 0xfb57...fca6.

  • Step 1: The attacker flash-loaned ~358,681e18 WBNB.

  • Step 2: The attacker purchased 2e17 MT, thereby bypassing the buy restriction.

  • Step 3: The attacker supplied 4e12 WBNB and 2e17 MT to the Pair to add liquidity. This transfer bypassed the fee-charging logic for the same reason as above.

  • Step 4: The attacker bought ~10,000,000e18 MT tokens from the Pair to the Router, thereby bypassing both the buy restriction and the fee-charging logic.

  • Step 5: The attacker removed half of their liquidity position, extracting all MT tokens held by the Router in the process, and then sold the recovered MT for WBNB. In this step, pendingBurnAmount was manipulated to approximately 9,000,000e18.

  • Step 6: The attacker bought ~10,000,000e18 MT tokens again, driving the pool's MT reserve down to ~6,756,516e18, which was lower than pendingBurnAmount.

  • Step 7: The attacker removed the remaining half of their liquidity position, withdrew the purchased MT tokens, and then called distributeDailyRewards() to burn MT from the pool. As a result, the MT reserve was reduced to 21,000e18.

  • Step 8: The attacker swapped all MT back for ~1,198e18 WBNB, repaid the flash loan, and finalized the profit.

Conclusion

This exploit was caused by incorrect trading restrictions, which allowed the attacker to bypass the buy ban by purchasing exactly BINDING_AMOUNT of MT tokens. After obtaining MT tokens, the attacker was further able to bypass both the fee-charging logic and the buy restriction by first adding liquidity, then buying MT into the Router, and finally removing liquidity to retrieve the tokens. The attacker then accumulated pendingBurnAmount via sell operations and executed the burn to push the pool reserves into an abnormal state, enabling them to sell MT at an inflated price and profit.

To reduce similar risks in the future:

  • Enforce strict separation between transfer semantics and trading logic.

AAVE Liquidation Incident

Brief Summary

On March 11, 2026, AAVE suffered $21M in incorrect liquidations on Ethereum, resulting in a loss of ~$1.01M. The root cause was an incorrect oracle price for wstETH, which caused positions that were originally healthy to become undercollateralized. As a result, users' positions were liquidated, leading to financial losses.

Background

AAVE uses oracle adapters to price wrapped assets like wstETH. The adapter CAPO (Capped Price Oracle) derives the wstETH price by multiplying the base ETH/USD price by a conversion ratio (getRatio(), i.e., how much ETH one wstETH is worth). To prevent ratio manipulation, CAPO applies a snapshot-based growth cap:

maxRatio = snapshotRatio + maxGrowthPerSecond x (currentTime - snapshotTimestamp)

and clamps the getRatio() output during pricing (if currentRatio > maxRatio, it uses maxRatio). This mechanism effectively limits the maximum upward drift of the ratio and the resulting price.

Vulnerability Analysis

The root cause was a time-ratio mismatch in the CAPO oracle anchor configuration (0xe1D9...61Ef): the snapshot timestamp and snapshot ratio were set, but the snapshot ratio was configured below the true wstETH/ETH ratio. As a result, the adapter's computed maxRatio fell below the live ratio and clamped getRatio() downward, systematically undervaluing the wstETH/USD oracle price. This depressed collateral valuation reduced the health factor of positions using wstETH as collateral, causing otherwise healthy accounts to be incorrectly classified as unhealthy and liquidated.

Attack Analysis

The following analysis is based on the transaction 0x9064...8a9c.

  • Step 1: The liquidator flash-loaned ~6304e18 WETH and liquidated the borrower.

  • Step 2: The liquidator repaid the flash loan, completing the liquidation.

Conclusion

This liquidation was caused by an incorrect oracle price configuration, which incorrectly pushed borrowers who should have remained healthy into an unhealthy state, thereby triggering liquidation of their positions.

To reduce similar risks in the future:

  • Ensure critical parameters are verified for correctness before each update.

  • Add validation checks in the implementation to reject incorrect parameters and prevent incorrect configurations from being applied successfully.


Planet Finance Incident

Brief Summary

On March 11, 2026, Planet Finance was exploited on BNB Chain, resulting in an estimated loss of ~$10K. The root cause was that the protocol mistakenly treated increases in a borrower's stored borrow balance as accrued interest, allowing an attacker to repeatedly borrow and trigger discount settlement to understate their recorded debt.

Background

Planet Finance is a lending protocol that allows borrowers to repay with an interest discount. The discount is tiered and is determined by the ratio between a user's staked GAMMA and their staked value in other assets: the higher this ratio, the higher the repayment discount. The discount schedule comprises three tiers, ranging from 0% (minimum) to 50% (maximum).

Vulnerability Analysis

The root cause was that when settling a borrower's discount in changeUserBorrowDiscount(), the protocol (0x4c9E...F467) mistakenly treated the increase in the borrower's stored borrow balance as newly accrued interest. As a result, the discount intended to apply only to accrued interest was incorrectly applied to the newly borrowed principal, improperly reducing the borrower's recorded debt. An attacker could repeatedly perform the borrow then changeUserBorrowDiscount loop to accumulate excessive discounts, causing the on-chain recorded liability to be consistently lower than the true borrowed amount, and ultimately profit from the discrepancy.

Attack Analysis

The following analysis is based on the transaction 0x5f45...5ec9.

  • Step 1: The attacker flash-loaned 200,000e18 USDT.

  • Step 2: The attacker used 5,000e18 USDT to purchase WBNB, and then used the acquired WBNB to buy ~8,726,524e18 GAMMA.

  • Step 3: The attacker first staked all acquired GAMMA into the gGAMMA market, then supplied the remaining USDT as collateral, which increased their repayment discount to 5% and enabled subsequent borrowing.

  • Step 4: The attacker repeatedly called borrow and then updateUserDiscount to continuously reduce their recorded debt.

  • Step 5: The attacker ultimately repaid the debt, redeemed the collateral, and realized the profit.

Conclusion

This incident was caused by Planet Finance's flawed discount-settlement logic in changeUserBorrowDiscount(), which mistakenly treats the increase in a borrower's stored borrow balance as newly accrued interest and applies the interest discount to that delta. An attacker can repeatedly call borrow followed by updateUserDiscount to understate their recorded debt and ultimately repay less than the true liability to extract profit.

To reduce similar risks in the future:

  • Distinguish interest and new borrows in the lending protocol.

AM Incident

Brief Summary

On March 12, 2026, AM Token, a deflationary token on BNB Chain, was exploited with an estimated loss of ~$131K. AM Token implements a deflationary mechanism where each sell triggers an additional burn from the liquidity pool, permanently removing tokens to reduce total supply. However, the burn is not executed immediately - instead, the full sell amount is recorded as toBurnAmount and the actual burn is deferred to the next sell. This delay creates a window between recording and execution, during which an attacker can buy back AM to shrink the pool's AM reserve down to toBurnAmount. When the next sell triggers the deferred burn, the entire AM reserve is wiped out, driving the price to an extreme level and allowing the attacker to sell AM for profit.

Background

AM Token is a deflationary token on BNB Chain. On each sell, the contract records the amount of AM involved in the swap as toBurnAmount, and burns that recorded amount from the liquidity pool during the next sell. In effect, sells trigger a delayed burn that shrinks the pool's AM reserve. In addition, before executing the burn, the protocol swaps the accumulated totalTokenFee into USDT and distributes it according to its fee-allocation logic.

Vulnerability Analysis

The root cause was that the token's (0x27f9...213f) sell logic accumulates the full swapped AM amount as toBurnAmount and only executes the burn on the next sell by removing tokens from the AM/USDT pair and calling pair.sync() to update reserves. This design allows an attacker to manipulate the pool's AM reserves, distort the on-chain price, and profit via arbitrage.

Attack Analysis

The following analysis is based on the transaction 0xd0d1...f859.

  • Step 1: The attacker flash-loaned ~27,265,119e18 USDC and ~361,710e18 WBNB, then swapped them for ~100,423,811e18 USDT.

  • Step 2: The attacker swapped ~5,062e18 AM tokens for USDT, which manipulated the contract's recorded toBurnAmount to ~4,303e18.

  • Step 3: The attacker swapped USDT for AM Token, pushing the pool's AM reserve down to ~4,303e18.

  • Step 4: The attacker transferred 6 wei AM to the pool, triggering the sell-path burn logic. As a result, the contract burned the entire AM balance from the pool, driving the AM reserve down to 0. Note: the protocol first attempts to swap the accumulated fees into USDT before the burn. This fee-conversion path also triggers the sell-branch burn logic. After the burn executes and the AM reserve reaches 0, the fee swap fails. Because it is wrapped in a try/catch, the failure does not revert the transaction. Instead, execution continues and the fee accumulator is reset to 0.

  • Step 5: The attacker called pool.sync() and transferred the remaining USDT and 1 wei AM into the pool. Because both tokens were transferred simultaneously, the contract treated this as addLiquidity, so toBurnAmount was not accumulated. The AM reserve was updated to 7.

  • Step 6: The attacker swapped the remaining AM tokens for USDT. During this swap, transferring AM into the Pair triggered the sell-path burn logic, reducing the AM reserve to 1. In addition, because totalFeeAmount had been reset to 0 in Step 4, the fee-to-USDT conversion was no longer executed, allowing the attacker to sell AM at an artificially inflated price.

  • Step 7: The attacker repaid the flash loan and realized the remaining profit.

Conclusion

This incident was caused by AM Token's flawed burn mechanism, which accumulates each sell's swap-involved AM as toBurnAmount and then burns that amount from the AM/USDT Pair on the next sell while calling pool.sync(). This allows an attacker to manipulate the Pair's AM reserves down to an extreme level and sell AM at an artificially inflated price to drain USDT.

To reduce similar risks in the future:

  • Limit the maximum burn amount per transaction and rate-limit how frequently burn can be triggered, preventing attackers from consuming a large portion of the pool's token reserves within a short period of time.

DBXen Incident

Brief Summary

On March 12, 2026, DBXen, a burn-to-earn protocol on Ethereum and BNB Chain, was exploited with a total loss of ~$149K. The root cause was an inconsistency between _msgSender() and msg.sender. When burnBatch() is called through the forwarder, the burned XEN amount is recorded under _msgSender() (attacker-controlled), but the cycle records are updated on msg.sender (the forwarder). This split allows the attacker to claim rewards and fees against stale cycle records, resulting in abnormally large payouts.

Background

DBXen is a burn-to-earn protocol: users burn XEN tokens in exchange for DXN rewards and a share of accumulated protocol fees. The key mechanism works in cycles. When a user calls burnBatch(), two things happen: (1) the burned XEN amount is recorded under the caller's address (identified by _msgSender()), and (2) the XEN contract calls back into DBXen via onTokenBurned() to update the caller's cycle records (burn cycle and lastFeeUpdateCycle) to the current cycle.

Rewards and fees are settled via updateStats(). The reward is proportional to the user's share of total XEN burned in their burn cycle. The fee is based on the cumulative protocol fees that accrued since the user's last recorded cycle. Both calculations depend on the user's cycle records being up to date.

Vulnerability Analysis

The root cause is flawed business logic in the DBXen protocol (0xf5c8...2abd). The _msgSender() function checks whether msg.sender is the forwarder. If so, it returns the last 20 bytes of calldata, and this returned value can be arbitrarily controlled in the forwarder context. However, burnBatch() directly burns the XEN held by msg.sender. As a result, an attacker can invoke burnBatch() through the forwarder, causing the protocol to burn the XEN held by the forwarder and update both the forwarder's burn-cycle record and fee-update cycle record to the current cycle. At the same time, however, the protocol records the burned XEN amount under the address corresponding to _msgSender().

After that, the attacker calls claimFees(), which invokes updateStats(). Because the _msgSender() address's cycle records were never updated (both burn cycle and lastFeeUpdateCycle remain at 0), updateStats() calculates rewards in the current cycle and computes fees accumulated since cycle 0 - spanning the protocol's entire fee history. The attacker then profits by calling claimFees() and claimRewards().

Attack Analysis

The following analysis is based on the transaction 0x914a5a...b808bc37.

  • Step 1: The attacker first called the registerDomainSeparator() function of the Forwarder contract, enabling subsequent calls to Forwarder.execute().

  • Step 2: The attacker swapped 0.14e18 ETH for 13,900,000,000e18 XEN in a Uniswap V2 pool.

  • Step 3: The attacker transferred 13,900,000,000e18 XEN to the Forwarder contract.

  • Step 4: The attacker used Forwarder.execute() to approve DBXen to spend the 13,900,000,000e18 XEN held by the Forwarder.

  • Step 5: The attacker used Forwarder.execute() to call DBXen.burnBatch() and burned 13,900,000,000e18 XEN. The burn amount was recorded under the address 0x425D3eC2DCeBE2c04bA1687504D43AFC6be7328d, while during the burn execution, XEN called back into DBXen via onTokenBurned(), updating the relevant cycle records on the Forwarder.

  • Step 6: The attacker used Forwarder.execute() to call DBXen.claimFees() and obtained 65.36e18 ETH.

  • Step 7: The attacker used Forwarder.execute() to call DBXen.claimRewards() and minted 2,305.4e18 DXN.

Conclusion

The root cause of this incident was that the DBXen protocol inconsistently used _msgSender() and msg.sender. Because these two values could differ, the protocol's internal accounting became inconsistent, which allowed the attacker to exploit the discrepancy for profit.

To reduce similar risks in the future:

  • Use _msgSender() consistently throughout all logic paths, or ensure that msg.sender-dependent operations and _msgSender()-dependent accounting always reference the same address.

Goose Finance Incident

Brief Summary

On March 15, 2026, Goose Finance, a yield-farming protocol on BNB Chain, was exploited for about $8K. The root cause was a share-pricing order flaw in StrategyGooseEgg: deposit() mints shares before settling harvested rewards into the accounting, so the total-asset denominator used for share pricing excludes these rewards and is lower than the true value. This means depositors receive more shares than they should. When withdraw() is called, it triggers a reward harvest that increases the total assets, making each share worth more. By looping deposit and withdraw in a single transaction, the attacker repeatedly minted over-priced shares and redeemed them at the corrected (higher) value, extracting the difference as profit.

Background

Goose Finance is a BNB Chain yield-farming protocol where user funds flow from a vault into a strategy, which stakes assets in MasterChef to earn EGG rewards.

The components relevant to this incident are:

  • VaultChef (0x3f64...): tracks user positions and forwards capital to StrategyGooseEgg.

  • StrategyGooseEgg (0x0980...): maintains strategy-level accounting with sharesTotal and wantLockedTotal.

  • MasterChef (0xe70e...): receives staked assets and pays EGG rewards.

  • WrappedEgg (0xb815...): wraps EGG 1:1 into WEGG for staking.

Operationally, deposits are routed from VaultChef to StrategyGooseEgg, then staked into MasterChef. Withdrawals are initiated from VaultChef and executed by the strategy.

A key accounting expectation is that share pricing should reflect total strategy assets at pricing time (staked principal plus idle rewards already held by the strategy). In StrategyGooseEgg, however, deposit() mints shares before _farm() settles idle assets into wantLockedTotal, while withdraw() can trigger reward harvest from MasterChef. This order is the basis of the vulnerability analyzed below.

Vulnerability Analysis

The root cause is an accounting desynchronization in StrategyGooseEgg (0x0980...b26b) between reward harvest and share pricing.

In StrategyGooseEgg, share pricing uses wantLockedTotal as the denominator: shares = deposit * sharesTotal / wantLockedTotal. For this to be fair, wantLockedTotal must reflect all assets the strategy actually holds, including any idle EGG rewards sitting in the contract. However, deposit() mints shares before _farm() settles idle rewards into wantLockedTotal. This means the denominator excludes unaccounted rewards and is lower than the true total assets, causing the depositor to receive more shares than they should.

Furthermore, withdraw() calls MasterChef.withdraw(), which returns staked principal plus pending EGG rewards to the strategy. The strategy's accounting only subtracts the requested _wantAmt from wantLockedTotal, so the harvested rewards remain on the strategy's balance without being reflected in wantLockedTotal. This widens the gap between the actual assets held and the recorded wantLockedTotal, making any subsequent deposit() share pricing even more inaccurate.

Attack Analysis

The following analysis is based on transaction 0x86efdf...ce316223.

  • Step 1: The attacker flash-borrows EGG from two Pancake pairs.

  • Step 2: First deposit into VaultChef/StrategyGooseEgg (10,170,000e18 EGG).

  • Step 3: First withdraw (12,593,884e18 EGG) harvests rewards from MasterChef; 359,561e18 EGG is transferred to StrategyGooseEgg and remains as idle/unaccounted value (R > 0).

  • Step 4: Second deposit reuses the withdrawn capital (12,593,884e18 EGG). Shares are priced before idle value is settled, so this is the over-mint step.

  • Step 5: Second withdraw (12,826,027e18 EGG) realizes profit from over-minted shares (i.e., 232,143 EGG above step-4 deposit input).

  • Step 6: The attacker repays flash swaps and keeps the net spread.

Conclusion

The exploit stems from a share-pricing order flaw in StrategyGooseEgg: deposit() mints shares before _farm() updates wantLockedTotal, while withdraw() can harvest rewards from MasterChef that remain temporarily idle and unaccounted. This allows deposits to mint against a stale denominator and later withdraw against updated assets.

To reduce similar risks in the future:

  • Settle rewards and update accounting before both share-mint and share-burn calculations.

  • Price shares against a single totalAssets (staked + idle) at the exact calculation point.

  • Add invariant tests for shares_minted <= D * S / (A + R) under non-zero idle reward conditions.


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
Building a Secure Stablecoin Payment Network: BlockSec Partners with Morph
Partnership

Building a Secure Stablecoin Payment Network: BlockSec Partners with Morph

BlockSec has partnered with Morph as an official audit partner for the $150M Morph Payment Accelerator. By offering exclusive discounts on smart contract audits and penetration testing, BlockSec provides institutional-grade security to payment builders, ensuring a safe and resilient foundation for the future of global stablecoin payments.

Venus Thena (THE) Incident: What Broke and What Was Missed

Venus Thena (THE) Incident: What Broke and What Was Missed

On March 15, 2026, an attacker bypassed the THE (Thena) supply cap on Venus Protocol (BNB Chain) through a donation attack, inflating a collateral position to 3.67x the intended limit and borrowing ~$14.9M in assets. Both sides lost money on-chain: Venus was left with ~$2.15M in bad debt after 254 liquidation bots competed across 8,048 transactions, while the attacker retained only ~$5.2M against a $9.92M investment. This deep dive examines what broke across three lines of defense (exposure limits, collateral valuation, and liquidation) and the monitoring gaps that left months of on-chain warning signals unacted upon.

Tether Freezes $6.76M USDT Linked to Iran's IRGC & Houthi Forces: Why On-Chain Compliance is Now a Geopolitical Battlefield
Security Insights

Tether Freezes $6.76M USDT Linked to Iran's IRGC & Houthi Forces: Why On-Chain Compliance is Now a Geopolitical Battlefield

Looking ahead, targeted freezing events like this $6.76M USDT action will only become more common. On-chain data analysis is improving. Stablecoin issuers are also working closely with regulators. As a result, hidden illicit financial networks will be exposed.