# BetaPresale: public set() plus per-sender deposit cap drains presale

BetaPresale's public set() let attacker redirect withdrawals; 70 fresh depositors each bypassed the 100 BNB per-sender cap, minting BETA sold for profit.

- Payout: Real-world hack
- Severity: high
- Program: BetaPresale
- Credited: DeFiHackLabs
- Disclosed: 2025-05-19
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2025-05/BetaPresale_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20250519-betapresale
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity technique:chained vuln:access-control vuln:business-logic vuln:flash-loan

**Target:** PresaleBEP20 presale contract on BSC
**Root cause:** PresaleBEP20.set(address) is publicly callable and mutates withdrawAddress, while deposit() enforces the 100 BNB max only per msg.sender and immediately forwards BNB to the mutable withdrawAddress.
**Steps:**
1. Flash loan 100 WBNB from DODO pool `0x6098...B476`.
2. Withdraw WBNB to BNB via `IWBNB.withdraw(100 ether)`.
3. Loop 70 times: deploy fresh `BetaDepositCycler`, call `PRESALE.set(attacker)` then `PRESALE.deposit{value:100 ether}()`; each new msg.sender bypasses the per-sender cap and BNB is forwarded to attacker.
4. Sweep minted BETA from each cycler to attacker.
5. Sell 7000 BETA into BETA/BUSD pair via `swap(0, busdOut, profitReceiver, '')`.
6. Sell remaining BETA into BETA/WBNB pair similarly.
7. Re-deposit 100 BNB to WBNB and repay DODO flash loan.
**How found:** Reading PresaleBEP20 source: noticed set() lacked access control and deposit() capped only per msg.sender while forwarding funds to the mutable withdrawAddress, enabling recycling of the same capital across fresh addresses.
**Exploitation:** Attacker flash-borrowed 100 WBNB, unwrapped to BNB, and used 70 freshly deployed depositor contracts to each call the public set() to point withdrawAddress at the attacker, then deposit() 100 BNB. Because the cap was per-msg.sender and BNB was forwarded to the mutable withdrawAddress, the same 100 BNB was recycled 70 times, minting BETA each round. Minted BETA was dumped into BETA/BUSD and BETA/WBNB PancakeSwap pairs for ~1.92 WBNB and ~46.47 BUSD profit, then the flash loan was repaid.
**Escalation:** Per-sender cap bypassed by deploying 70 fresh depositor contracts, multiplying minted BETA ~70x from a single 100 BNB float.
**Insight:** When a deposit cap is keyed on msg.sender but funds are forwarded to a caller-settable address, deploy fresh depositor contracts to recycle the same capital; also check whether the withdrawal-address setter is access-controlled.
