# Reentrancy bug in Basket’s withdraw multiple tokens function which gives attacker ability to transfer basket ownership and spend it but withdraw all the tokens out of basket

Basket's batch withdraw checks NFT ownership only once, letting a malicious first token re-enter, sell the basket as a vault, then drain remaining NFTs.

- Payout: Audit contest
- Severity: medium
- Program: Nibbl
- Found by: unforgiven
- Disclosed: 2022-08-01
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2022-06-nibbl#m-10
- Library page: http://bugbountylibrary.com/r/c4%3A2022-06-nibbl%3AM-10
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:chained vuln:access-control vuln:reentrancy

**Target:** Basket.sol batch withdraw functions (withdrawMultipleERC721/ERC1155/ERC20)
**Root cause:** withdrawMultiple* checks _isApprovedOrOwner(msg.sender, 0) only once before looping external safeTransferFrom calls, so ownership can change mid-loop while the drain continues.
**Steps:**
1. Create Basket_M holding well-known NFTs.
2. Approve NibblVaultFactory for Basket_M id=0.
3. Call `Basket_M.withdrawMultipleERC721(_tokens, _tokenId, _to)` with attacker contract as first entry in `_tokens`.
4. Ownership check passes; first iteration calls attacker contract via `safeTransferFrom`.
5. Attacker contract calls `NibblVaultFactory.createVault()` with Basket_M id=0, transferring basket ownership to Vault_M.
6. Attacker buys shares of Vault_M and induces a third-party Invest_Contract to buyout/invest based on on-chain checks that Basket_M holds the NFTs.
7. Attacker withdraws funds from Vault_M.
8. Remaining loop iterations transfer all Basket_M NFTs to attacker; basket left empty.
**How found:** Code review of batch withdraw functions: noticed ownership is validated once before a loop of external safeTransferFrom calls, enabling state change mid-loop.
**Exploitation:** Attacker uses the mid-loop callback to convert the basket into a vault and extract funds from a third-party contract that authorizes purchases from on-chain state, then lets the original batch withdraw finish draining the NFTs.
**Escalation:** Chains reentrancy with vault creation and third-party buyout logic to monetize the basket before draining it.
**Insight:** Batch functions that check authorization once then loop external transfers are reentrancy-prone; a malicious entry in the token array becomes the callback hook, so audit array-driven loops for per-iteration auth.

