Calculated from recorded token losses using historical USD prices at the incident time.
0xdf6252854362c3e96fd086d9c3a5397c303d265649aee0b023176bb49cf00d4b0x39e29f4fb13aec505ef32ee6ff7cc16e2225b11fBSCOn BNB Smart Chain block 26834150, attacker EOA 0x586094fcafb76c3d9a209c58301802ce243269b8 deployed helper contract 0x31383bcb3d234b26dee0ff0adf83b47fe99b8e3d and used it to call unstake(address,uint256,address,bool) on Thena USDC-wUSDR strategy proxy 0x39e29f4fb13aec505ef32ee6ff7cc16e2225b11f. That single public call harvested rewards, unwound the strategy's gauge and LP position, swapped the resulting assets into USDT, and routed 10197886482010621829898 USDT to the attacker cluster.
The root cause is a missing authorization check on the proxy implementation's unstake(address,uint256,address,bool) entrypoint. Comparable administrative functions are role-gated, but the destructive unwind path is not. Because the same call succeeds from arbitrary EOA 0x1111111111111111111111111111111111111111 against the public pre-state at block 26834149, the exploit is a deterministic ACT opportunity rather than an attacker-specific replay.
The victim entrypoint is TransparentUpgradeableProxy 0x39e29f4fb13aec505ef32ee6ff7cc16e2225b11f. Historical storage at the EIP-1967 implementation slot resolves the proxy to implementation 0xaedb00947b0bfd2723898f78018b4bb7b2398cdc at the incident block. The strategy held value in a Thena farm configuration built around:
0x2e537237143abf74a176d0067beebeebe845300a0xa99c4051069b774102d6d215c6a9ba69bd616e6a0x2952beb1326accbb5243725bd4da2fc937bca0870x8ac76a51cc950d9822d68b83fe1ad97b32cd580d0x55d398326f99059ff775485246999027b31979550xf4c8e32eadec4bfe97e0f595add0f4450a863a11The strategy's public pre-state at block 26834149 still held positive NAV, LP, reward inventory, and withdrawable assets. That matters because the vulnerable unstake path does not just redeem a caller's own position; it liquidates strategy-held assets and sends the output token to a caller-chosen recipient.
The bug is an access-control failure in a privileged asset-management function. Runtime disassembly of implementation 0xaedb00947b0bfd2723898f78018b4bb7b2398cdc shows that setSlippages(uint256,uint256,uint256) and setParams(...) perform role checks before mutating strategy state, while unstake(address,uint256,address,bool) jumps into the unwind logic without any hasRole(...) or equivalent authorization gate. The violated invariant is straightforward: only authorized portfolio-management actors should be able to unwind strategy positions and withdraw resulting assets from the proxy.
The code-level breakpoint is the unstake branch beginning at offset 0x13db. From there the implementation reads NAV, harvests rewards, removes gauge liquidity, burns LP, swaps underlying assets into a caller-selected output token, and finally performs an ERC20 transfer to the caller-supplied recipient at offset 0x1515. The exploit works because the function accepts both the destination token and recipient as untrusted inputs while remaining reachable from any unprivileged caller.
Disassembly summary from the validated collector artifacts:
- setSlippages starts at 0x0d6e and checks PORTFOLIO_AGENT_ROLE.
- setParams starts at 0x0eb5 and checks DEFAULT_ADMIN_ROLE.
- unstake starts at 0x13db and performs no hasRole(...) check.
- The unwind path reaches an ERC20 transfer to the caller-supplied recipient at 0x1515.
The exploit transaction 0xdf6252854362c3e96fd086d9c3a5397c303d265649aee0b023176bb49cf00d4b creates helper contract 0x31383bcb3d234b26dee0ff0adf83b47fe99b8e3d, which immediately calls:
0x39E29f4FB13AeC505EF32Ee6Ff7cc16e2225B11F::unstake(
0x55d398326f99059fF775485246999027B3197955,
0,
0x31383BCB3D234b26deE0Ff0Adf83B47Fe99B8E3D,
true
)
The validated trace shows the full liquidation path that the unguarded function makes public:
GaugeV2::getReward()
emit Transfer(from: GaugeV2, to: strategy, value: 254630019576436463378)
Router::removeLiquidity(..., 156444250371137897, ...)
emit Transfer(from: GaugeV2, to: strategy, value: 156444250371137897)
emit Transfer(from: Pair, to: strategy, value: 5061189125337114043869)
Router::swapExactTokensForTokens(... USDC -> USDT ...)
emit Transfer(from: Pair, to: strategy, value: 10089706261008774455811)
BEP20USDT::transfer(helper, 10089706261008774455811)
That trace aligns exactly with the balance-diff artifact. During the exploit:
25463001957643646337815644425037113789720139972262 to 010197886482010621829898The final forwarding step is explicit in the same trace:
BEP20USDT::balanceOf(0x31383BCB3D234b26deE0Ff0Adf83B47Fe99B8E3D)
-> 10197886482010621829898
BEP20USDT::transfer(
0x586094FcaFB76c3D9a209c58301802CE243269B8,
10197886482010621829898
)
Independent historical simulation confirms the exploit does not require attacker-controlled addresses. Calling unstake(USDT, 0, 0x1111..., true) from arbitrary EOA 0x1111111111111111111111111111111111111111 against block 26834149 succeeds and returns 10089706261008774455811, which proves the vulnerable path is permissionlessly reachable in the public pre-state.
The adversary strategy is a single-transaction drain with three stages.
First, the attacker deploys helper contract 0x31383bcb3d234b26dee0ff0adf83b47fe99b8e3d from EOA 0x586094fcafb76c3d9a209c58301802ce243269b8. The helper is operational glue only; the root cause sits in the strategy implementation.
Second, the helper invokes the victim's public unstake(address,uint256,address,bool) entrypoint with token=USDT, minAmount=0, recipient=helper, and swapTokens=true. Because the implementation omits authorization, the strategy unwinds itself on behalf of the attacker: it harvests 254630019576436463378 THE, withdraws 156444250371137897 LP from the gauge, removes liquidity from the Thena pair, swaps wUSDR and USDC into USDT, and transfers two USDT payouts of 108180221001847374087 and 10089706261008774455811 to the helper.
Third, the helper forwards the combined 10197886482010621829898 USDT to the attacker EOA. The transaction receipt records gasUsed = 2049655 and effectiveGasPrice = 5000000000, so the exact gas cost is 10248275000000000 wei of BNB.
The measurable loss is 10197886482010621829898 USDT, recorded directly as the attacker EOA's balance increase in the validated balance-diff artifact. The strategy's pre-state holdings were effectively converted into transferable USDT and removed from the proxy. Supporting balance changes show that the strategy also lost all directly held wUSDR, all gauge-held LP, and the harvested THE rewards that were immediately sold during the unwind.
Because the exploit path is public and reproducible from an arbitrary EOA against the pre-state, every unit of strategy NAV exposed to unstake was permissionlessly withdrawable by any searcher able to submit the call first.
0xdf6252854362c3e96fd086d9c3a5397c303d265649aee0b023176bb49cf00d4b0x39e29f4fb13aec505ef32ee6ff7cc16e2225b11f0xaedb00947b0bfd2723898f78018b4bb7b2398cdc26834149:unstake(
0x55d398326f99059ff775485246999027b3197955,
0,
0x1111111111111111111111111111111111111111,
true
) -> 10089706261008774455811
0x31383bcb3d234b26dee0ff0adf83b47fe99b8e3d0x586094fcafb76c3d9a209c58301802ce243269b80x2e537237143abf74a176d0067beebeebe845300a0xa99c4051069b774102d6d215c6a9ba69bd616e6a0x55d398326f99059ff775485246999027b31979550x8ac76a51cc950d9822d68b83fe1ad97b32cd580d0x2952beb1326accbb5243725bd4da2fc937bca0870xf4c8e32eadec4bfe97e0f595add0f4450a863a11