# NovaBox: constructor bypass of extcodesize guard drains dividends

NovaBox's extcodesize deposit guard was bypassed via constructor, letting attacker join dividend list with zero checkpoints and drain 56.73 ETH in stale dividends.

- Payout: Real-world hack
- Severity: high
- Program: NovaBox
- Credited: DeFiHackLabs
- Disclosed: 2026-06-09
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2026-06/NovaBox_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20260609-novabox
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** Solidity dividend/staking contract with ETH+NOVA dual deposits
**Root cause:** ETH deposits are gated by extcodesize(msg.sender)==0, which is zero during contract construction; new dual ETH/NOVA depositors are added to the dividend list without initializing their dividend checkpoints, so they inherit stale historical ETH dividends.
**Steps:**
1. Flash-loan 427.5 WETH from Aave V3 via `flashLoanSimple`.
2. `weth.withdraw(amount)` to get ETH.
3. Deploy `NovaConstructorHelper{value: amount}` so its constructor runs with no runtime code.
4. In constructor: pull NOVA seed, `nova.approve(NovaBox, bal)`, call `box.depositTokens(address(this), 0)` to join dividend list with zero checkpoints.
5. Still in constructor, send ETH via low-level `call{value: msg.value}` to NovaBox — passes `extcodesize==0` check.
6. Call `box.withdrawEth(box.contributionsEth(address(this)))` to pull credited ETH plus stale historical dividends.
7. Repay Aave principal+premium in WETH, forward remaining ETH profit to attacker.
**How found:** Likely spotted that the deposit guard used extcodesize(msg.sender)==0 (bypassable from a constructor) and that the dividend-list join path for dual ETH/NOVA depositors skipped checkpoint initialization, allowing retroactive dividend claims.
**Exploitation:** Attacker flash-loans 427.5 WETH, unwraps to ETH, and deposits through a constructor helper that has no runtime code, bypassing the extcodesize guard. The helper joins the dividend list with uninitialized checkpoints, then immediately withdraws, collecting stale historical ETH dividends. Net 56.73 ETH profit after Aave repayment.
**Insight:** extcodesize-based 'no contracts' guards are always bypassable from a constructor — check whether the guarded path also skips state initialization (checkpoints, reward debt, last-update timestamps) that lets a fresh entrant claim historical rewards.
