# Malicious User Could Burn The Assets After A Successful Migration

Migration.migrateVaultERC20/721/1155 lack proposalId validation; a non-existent proposal resolves newVault to address(0), letting anyone burn vault assets post-migration.

- Payout: Contest reward
- Severity: high
- Program: Fractional
- Credited: xiaoming90
- Disclosed: 2022-09-27
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-07-fractional-findings/issues/459
- Library page: https://bugbountylibrary.com/r/c4%3A2022-07-fractional%3AH-09
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic

**Target:** Migration.sol migrateVaultERC20/ERC721/ERC1155 and Buyout.withdrawERC20/721/1155
**Root cause:** migrationInfo[_vault][_proposalId].newVault returns address(0) for a non-existent proposalId, and the code never checks newVault before instructing Buyout to withdraw assets to it.
**Steps:**
1. Alice completes migration: propose, join, commit, settleVault, settleFractions; vault A holds an APE ERC721.
2. Attacker calls `Migration.migrateVaultERC721(vaultA, invalidProposalId, apeNFT, tokenId, proof)`.
3. Inside, `newVault = migrationInfo[vaultA][invalidProposalId].newVault` resolves to `address(0)`.
4. Migration calls `IBuyout(buyout).withdrawERC721(vaultA, apeNFT, address(0), tokenId, proof)`; msg.sender==proposer passes NotWinner check.
5. Vault executes ERC721TransferFrom to address(0); custom ERC721/ERC20/1155 implementations without zero-address checks burn the asset.
**How found:** Code review of the migration flow: noticed migrateVault* reads newVault from a mapping without validating the proposalId, and that a missing key yields address(0), which is then passed as the transfer recipient.
**Exploitation:** After a successful migration, any user calls the permissionless migrateVault* functions with a bogus proposalId. Because the Migration contract is the buyout proposer, the NotWinner check passes, and the vault transfers its tokens to address(0), permanently burning them.
**Insight:** When a mapping lookup feeds a transfer recipient, test uninitialized keys: default zero values can become a burn address. Also check whether the calling contract is privileged (here it is the buyout proposer), which bypasses winner checks for anyone.
