# OpenLeverage RewardVaultDelegator: re-initialize to admin, drain via delegatecall

OpenLeverage RewardVaultDelegator's unprotected initialize() let attacker become admin, set malicious implementation, and drain ~$8K via delegatecall.

- Payout: Real-world hack
- Severity: high
- Program: OpenLeverage
- Analysis by: DeFiHackLabs
- Disclosed: 2023-10-22
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2023-10/OpenLeverage_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20231022-openleverage
- Tags: area:web3 domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:misconfiguration

**Target:** RewardVaultDelegator upgradeable proxy on BSC
**Root cause:** initialize() lacked a one-time guard, so anyone could re-initialize and set admin; setImplementation then allowed swapping in attacker-controlled logic executed via delegatecall.
**Steps:**
1. Call `RewardVaultDelegator.initialize(address(this), address(this), 1)` to become admin
2. Call `setImplementation(address(this))` to point proxy at attacker contract
3. Call `RewardVaultDelegator.a(address(this))` triggering delegatecall into attacker code
4. In `a()`, loop victims and call `transferFromAndSwapTokensToBNB(victim, token, attacker)`
5. For each victim: `IERC20(token).transferFrom(victim, this, min(balance, allowance))` then swap to BNB via PancakeSwap Router
6. Receive BNB in attacker contract
**How found:** Likely source review of the upgradeable RewardVaultDelegator proxy revealing initialize() without initializer modifier and a delegatecall entrypoint reachable by admin.
**Exploitation:** Attacker re-initialized the proxy to seize admin, replaced implementation, and used delegatecall to run arbitrary code in the proxy's storage context, pulling approved token balances from six victims (RACA, BUSDT, FLOKI, OLE, CSIX, BABY) and swapping to BNB.
**Insight:** On upgradeable proxies, always check whether initialize() is guarded by an initializer modifier and whether admin-only delegatecall entrypoints exist; a re-initializable proxy plus arbitrary delegatecall is a direct drain primitive.

