Thena Strategy Public Unstake Drain
Exploit Transactions
0xdf6252854362c3e96fd086d9c3a5397c303d265649aee0b023176bb49cf00d4bVictim Addresses
0x39e29f4fb13aec505ef32ee6ff7cc16e2225b11fBSCLoss Breakdown
Similar Incidents
Father Pepe Inu Unstake Drain
39%NeverFallToken LP Drain
35%MetaPoint Public Approval Drain
33%SafeMoon LP Burn Drain
33%QiQi Reward Quote Override Drain
32%SellToken Arbitrary-Pair LP Drain
32%Root Cause Analysis
Thena Strategy Public Unstake Drain
1. Incident Overview TL;DR
On 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.
2. Key Background
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:
- Gauge
0x2e537237143abf74a176d0067beebeebe845300a - Pair
0xa99c4051069b774102d6d215c6a9ba69bd616e6a - wUSDR
0x2952beb1326accbb5243725bd4da2fc937bca087 - USDC proxy
0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d - USDT
0x55d398326f99059ff775485246999027b3197955 - THE
0xf4c8e32eadec4bfe97e0f595add0f4450a863a11
The 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.
3. Vulnerability Analysis & Root Cause Summary
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.
4. Detailed Root Cause Analysis
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:
- Gauge-held THE decreases by
254630019576436463378 - Gauge-held LP decreases by
156444250371137897 - Strategy-held wUSDR falls from
20139972262to0 - Attacker EOA USDT increases by
10197886482010621829898
The 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.
5. Adversary Flow Analysis
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.
6. Impact & Losses
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.
7. References
- Seed exploit transaction:
0xdf6252854362c3e96fd086d9c3a5397c303d265649aee0b023176bb49cf00d4b - Victim proxy:
0x39e29f4fb13aec505ef32ee6ff7cc16e2225b11f - Historical implementation:
0xaedb00947b0bfd2723898f78018b4bb7b2398cdc - Arbitrary-EOA validation call at block
26834149:
unstake(
0x55d398326f99059ff775485246999027b3197955,
0,
0x1111111111111111111111111111111111111111,
true
) -> 10089706261008774455811
- Helper contract:
0x31383bcb3d234b26dee0ff0adf83b47fe99b8e3d - Attacker EOA:
0x586094fcafb76c3d9a209c58301802ce243269b8 - Supporting protocol components:
- Gauge
0x2e537237143abf74a176d0067beebeebe845300a - Pair
0xa99c4051069b774102d6d215c6a9ba69bd616e6a - USDT
0x55d398326f99059ff775485246999027b3197955 - USDC proxy
0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d - wUSDR
0x2952beb1326accbb5243725bd4da2fc937bca087 - THE
0xf4c8e32eadec4bfe97e0f595add0f4450a863a11
- Gauge