# The Association NFT 合约漏洞分析

By [analyse nfts](https://paragraph.com/@analyse-nfts) · 2022-04-24

---

漏洞描述
----

NBA发行的NFT项目`The Association NFT`，合约出现了白名单用户的签名数据被攻击者利用，可以无限制铸造NFT的重大漏洞，现在让我们看看这个合约的漏洞是怎么产生的.

漏洞位置
----

NBA这个发行的NFT项目有两个合约地址，一个是NFT本身的合约[The\_Association\_Token](https://etherscan.io/token/0x9b8b9c7c02be0bd0aa4d669bf6a1f6003424c6dc)，另一个是售卖合约[The\_Association\_Sales](https://etherscan.io/address/0xdd5a649fc076886dfd4b9ad6acfc9b5eb882e83c#code)，问题的根源出现在这个售卖合约上面。

售卖合约有个`mint_approved`方法，通过检验用户传送过来的签名数据，然后去调用[The\_Association\_Token](https://etherscan.io/token/0x9b8b9c7c02be0bd0aa4d669bf6a1f6003424c6dc)合约铸造NFT。

    function mint_approved(
            vData memory info,
            uint256 number_of_items_requested,
            uint16 _batchNumber
        ) external {
            require(batchNumber == _batchNumber, "!batch");
            address from = msg.sender;
            
            // info是包含用户签名的数据，通过调用verify方法去验证签名数据是否正确
            require(verify(info), "Unauthorised access secret");
    
            _discountedClaimedPerWallet[msg.sender] += 1;
            require(
                _discountedClaimedPerWallet[msg.sender] <= 1,
                "Number exceeds max discounted per address"
            );
            presold[from] = 1;
            
            // 如果上面verify方法验证通过，这里调用会NFT合约铸造NFT
            _mintCards(number_of_items_requested, from);
            emit batchWhitelistMint(_batchNumber, msg.sender);
        }
        
    

再看看`verify`方法它是如何检验签名数据的

    
        function verify(vData memory info) public view returns (bool) {
            require(info.from != address(0), "INVALID_SIGNER");
            bytes memory cat =
                abi.encode(
                    info.from,
                    info.start,
                    info.end,
                    info.eth_price,
                    info.dust_price,
                    info.max_mint,
                    info.mint_free
                );
            // mint数据的hash值
            bytes32 hash = keccak256(cat);
     
     
            require(info.signature.length == 65, "Invalid signature length");
            bytes32 sigR;
            bytes32 sigS;
            uint8 sigV;
            bytes memory signature = info.signature;
      
            // 通过签名获取到R,S,V
            assembly {
                sigR := mload(add(signature, 0x20))
                sigS := mload(add(signature, 0x40))
                sigV := byte(0, mload(add(signature, 0x60)))
            }
    
            bytes32 data =
                keccak256(
                    abi.encodePacked("\\x19Ethereum Signed Message:\
    32", hash)
                );
                
            // 通过mint数据的hash值,签名R,S,V获取到签名的所属用户地址
            address recovered = ecrecover(data, sigV, sigR, sigS);
            // 判断地址是合约配置的
            return signer == recovered;
        }
    

从上面代码可以看到，这个签名校验方法是没有对发送者`msg.sender`进行校验的，只对签名所属者和合约配置白名单地址进匹对检验,那么攻击者完全可以使用正常白名单的用户签名数据，绕过这个身份验证方法去铸造NFT。

链上复现
----

现在我们在ethscan上面看看哪些铸造NFT交易是攻击者发起的。

*   首先我们在ethscan上面查看售卖合约[The\_Association\_Sales](https://etherscan.io/address/0xdd5a649fc076886dfd4b9ad6acfc9b5eb882e83c#code)
    
*   有漏洞的铸造合约方法的id是`0x8df9da46`
    

p1

*   根据方法id对交易进行筛选
    

p2

*   定位到早期发起的一些交易记录。
    
*   现在我们来看这笔交易记录铸造tokenid为2489的[交易记录](https://etherscan.io/tx/0x75e6e7a504a6f370db1703056cd52a823e8dd4024cf16cfb08133503745f7d1c)
    

p3

p4

通过对交易输入的数据解析可以知道，红框圈住的输入数据，是白名单发起者的地址。

p5

*   现在我们再来看铸造tokenid为9657的这笔[交易记录](https://etherscan.io/tx/0x67b9f5508ca8c5a54065d7e6a101a581b1cfc77f46949a80804a372cf3af8000)
    

p6

p7

这里可以看到输入数据和上面的交易一模一样，但是发起者地址和输入数据的地址不符，说明这笔交易属于攻击者利用合约漏洞，使用正常用户的签名数据生成的。

另外对铸造方法分析，铸造NFT数量的参数`number_of_items_requested`不属于参数数据的一部分，那么修改这个参数值，可以实现一次性铸造多个。

预防措施
----

*   验证签名方法需要加入`msg.sender`校验
    
*   为了更节省存储gas费用，可以采用MerkleProof算法实现白名单功能
    

[https://github.com/MetaplasiaTeam](https://github.com/MetaplasiaTeam)

---

*Originally published on [analyse nfts](https://paragraph.com/@analyse-nfts/the-association-nft)*
