# StepHeroNFTs: reentrancy on sell NFT drains 137.9 BNB

Reentrancy in StepHeroNFTs sell function allowed attacker to repeatedly claim referral rewards and drain 137.9 BNB via flash loan.

- Payout: Real-world hack
- Severity: high
- Program: StepHeroNFTs
- Credited: DeFiHackLabs
- Disclosed: 2025-02-21
- Write-up quality: 7/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2025-02/StepHeroNFTs_exp.sol
- Library page: https://bugbountylibrary.com/r/dhl%3A20250221-stepheronfts
- Tags: domain:web3 impact:funds-loss kind:incident platform:defihacklabs sev:high tech:evm tech:solidity vuln:reentrancy

**Target:** StepHeroNFTs NFT marketplace contract on BSC
**Root cause:** The sell NFT function (selector 0xded4de3a) updates state after external call, allowing reentrancy. The claimReferral function also lacks reentrancy protection and can be called repeatedly during the fallback.
**Steps:**
1. Flash loan 1000 WBNB from PancakeV3 pool.
2. Withdraw WBNB to BNB.
3. Call vulnerable sell function on StepHeroNFTs with crafted parameters (selector `0xded4de3a`, id 2008, amount 6, etc.) sending BNB.
4. During the call, the contract sends BNB to attacker's receive() function.
5. In receive(), if msg.value == 3 ether, call `claimReferral(address(0))` to claim referral rewards.
6. Deploy AttackerC2 and call `buyAsset{value: 1000 ether}(81122, 1, msg.sender)` to buy NFT.
7. Call `claimReferral(address(0))` again to claim more rewards.
8. Repay flash loan with fee and transfer profit to attacker.
**How found:** Likely identified through source code review: the sell function performs an external call before updating state, and claimReferral lacks reentrancy guard. The attacker noticed that the receive() fallback could be triggered during the sell and used it to call claimReferral multiple times.
**Exploitation:** Attacker used a flash loan to obtain 1000 WBNB, converted to BNB, then interacted with StepHeroNFTs' sell function which had a reentrancy vulnerability. By reentering via the receive() fallback and calling claimReferral repeatedly, the attacker extracted referral rewards. The attacker also bought an NFT to further manipulate state. Finally, the loan was repaid and 137.9 BNB profit was sent to the attacker.
**Insight:** When auditing NFT marketplace contracts, check for reentrancy in sell/buy functions that send native currency; the receive() fallback can be used to reenter other functions like claimReferral if they lack guards. Also, referral reward functions are often overlooked and may be callable during reentrancy.
