# 复盘aishiba发币漏洞——项目方的低级bug,科学家的又一次狂欢 **Published by:** [berwinYes](https://paragraph.com/@coolberwin/) **Published on:** 2023-04-24 **URL:** https://paragraph.com/@coolberwin/aishiba-bug ## Content AIshiba作为arb上一个比较早期的土狗NFT,毫不夸张的说引燃了arb上的NFT潮流。floor price曾一度最高达到0.04e,但是现在已经跌倒了0.004e(大概8刀)。这就是妥妥的土狗项目,官网做的也是极其简陋,连白皮书都没有,只有路线图。但是没办法,赶上了风口,猪都能飞上天,赶上arb的aidog热度。 并且在今天凌晨进行nft空投发币,空投规则是按照快照时的地址NFT个数计算,即NFT拥有数量越多,空投claim的代币越多,nft空投上限10个,超过按照10个计算。 谁知道发币出现bug,导致科学家发现漏洞,单号直接mint最大量的token 17700000000000 * 10 ** 6 。最高点卖出大概1e+。 代币k线核心原因是项目方利用Merkle Tree发放 空投token,但是本应该在后台的保存了全量地址被项目方放在了前端,并且未做任何混淆代码的保护处理。导致proof被轻易计算得到。(了解原理可参阅文末参考文献) 只需要在官网按 F12 整个代码一览无余:merkleProof.js const { WHITELIST_ADDRESS } = require("./whitelist_Address") const keccak256 = require("keccak256") const { MerkleTree } = require("merkletreejs") const addressLeaves = WHITELIST_ADDRESS.map(x => keccak256(x)) const merkleTree = new MerkleTree(addressLeaves, keccak256, { sortPairs : true }) const rootHash = merkleTree.getHexRoot() module.exports = { merkleTree, rootHash } 并且调用合约的方法,传入参数也非常清晰:index.js const handleClaimForNfts = async () => { try { const signer = await getProviderOrSigner(true); const userAddress = signer.getAddress(); const aiShibaContract = new Contract( AISHIBA_CONTRACT_ADDRESS, AISHIBA_CONTRACT_ABI, signer ); const txn = await aiShibaContract.claimTokensForNft(merkleProof, nftBalance); await txn.wait(); console.log("txn", txn); console.log("txn", "successful"); } catch (error) { console.error(error); } }; 可以看到调用了合约的 claimTokensForNft() 方法,传入了merkleProof 和 nftBalance 两个变量,nftBalance 代表着快照拥有NFT的数量。 其中merkleProof 只需要 用下列方法 merkleTree.getHexProof(_leaf) 即可获取index.js const getProof = async () => { const signer = await getProviderOrSigner(true); const _userAddress = await signer.getAddress(); const _leaf = keccak256(_userAddress); const _merkleProof = merkleTree.getHexProof(_leaf); setUserAddress(_userAddress); setMerkleProof(_merkleProof); }; 再看一下领取的合约代码 function claimTokensForNft(bytes32[] calldata proof, uint256 _amount) public claimIsLive { bytes32 leaf = keccak256(abi.encodePacked(msg.sender)); require(MerkleProof.verify(proof, merkleRootNFT, leaf), "Wallet not eligible to claim"); require(_amount > 0, "You do not own any tokens"); require(!hasClaimedNFT[msg.sender], "You have claimed your Tokens"); uint256 amountOfTokensToClaim; if(_amount<= 2) { amountOfTokensToClaim = tier1Claim; } else if( _amount > 2 && _amount <= 4){ amountOfTokensToClaim = tier2Claim; } else if(_amount > 4 && _amount <= 9) { amountOfTokensToClaim = tier3Claim; } else if(_amount >= 10) { amountOfTokensToClaim = tier4Claim; } totalTokensClaimed += amountOfTokensToClaim; hasClaimedNFT[msg.sender] = true; require(tokenContract.transfer(msg.sender, amountOfTokensToClaim), "Transfer failed"); emit HasClaimedNFT(msg.sender, amountOfTokensToClaim); } _amount 即为nft的数量,合约按照 _amount 数量进行空投token。_amount<=2 空投代币 1475000000000 * 10 ** 62<_amount<=4 空投代币 3375000000000 * 10 ** 64<_amount<=9 空投代币 7750000000000 * 10 ** 6_amount>=10 空投代币 17700000000000 * 10 ** 6合约中没有对_amount的校验操作,即使你的钱包地址拥有1个nft(即拥有白名单权限),只要Merkle tree 验证成功,_amount传入10或者更大值也能mint 到单地址最高数量代币,即 17700000000000 * 10 ** 6 。 大概有1800个地址领取了空投,绝大部分都是科学家项目方发现了不对劲,在凌晨的00:45已经关闭了合约,并在接下来转走了合约中所有token。 据传言(道听途说):背后的项目方是大学生,整个寝室一起出动一起赚钱,如果真是这样,现在这大学生还真他娘的敢想敢干,割起韭菜来也是毫不手软。 可惜 螳螂捕蝉,黄雀在后,项目成了科学家的提款机,最可怜的还是那些真金白银在DEX和CEX中买Token的韭菜们。 不玩土狗的我 看着大佬们的卖币截图和聊天记录,又一次感慨道: 别人赚钱如呼吸般简单,自己赚钱如吃屎般困难。 本人推特:@coolberwin_eth 文章参考: Merkle tree的 原理: WTF Ethers极简入门: MerkleTree脚本 ## Publication Information - [berwinYes](https://paragraph.com/@coolberwin/): Publication homepage - [All Posts](https://paragraph.com/@coolberwin/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@coolberwin): Subscribe to updates - [Twitter](https://twitter.com/0xcoolberwin): Follow on Twitter