Damn Vulnerable DeFi 是一个 Defi 智能合约攻击挑战系列。内容包括了闪电贷攻击、借贷池、链上预言机等。在开始之前你需要具备 Solidity 以及 JavaScipt 相关的技能。针对每一题你需要做的就是保证该题的单元测试能够通过。
题目链接:https://www.damnvulnerabledefi.xyz/challenges/10.html
题目描述:
现有已经发布的 Damn Valuable NFT 交易市场,初始 mint 了 6 个 NFT,并且可以在交易市场上出售,售价为 15ETH。
有个买家告诉了你一个秘密:市场是脆弱的,所有的代币都可以被拿走。然而,他并不知道怎么做。为此愿意提供 45 ETH 的奖励给取出 NFT 并发送给他的人。
你想在这个买家那里建立一些名声,所以你已经同意了这个计划。
遗憾的是你只有 0.5 ETH。要是有一个地方你可以免费获得 ETH 就好了,暂时性的也可以。
FreeRiderBuyer.sol#
买家合约
由于该合约继承自 IERC721Receiver ,且 NFT 合约继承自 ERC721
意味着当 NFT 合约调用 safeTransferFrom 且 to 地址是该合约时,会触发 onERC721Received 函数。
safeTransferFrom 内部会判断 to 是否是合约地址,是的话则会调用 onERC721Received
FreeRiderNFTMarketplace.sol#
交易合约,继承自 ReentrancyGuard, 用于防止重入攻击。
构造函数中创建了一个 DamnValuableNFT 合约,并 mint 了 amountToMint 数量的 NFT 给了合约部署者
该合约提供了两种功能
- 批量出售
offerMany
NFT 持有人调用该方法,除了一些基本的校验之外,还会授权该合约可以对持有人持有的 NFT 进行操作。
最后将报价保存到 offers[tokenId],当有人购买时,由于持有人已经授权了该合约可以操作其所持有的 NFT,故在收到付款后,该合约可以直接将持有人的 NFT 转给购买人。
- 批量购买
buyMany
当有卖家在交易合约中出售其持有的 NFT 后,买家就可以执行购买流程,首先获取价格,并确保收到的 ETH 不低于售价,之后由该合约将卖家持有的 NFT 转给买家并将收到的 ETH 转给卖家。
批量购买功能其实有个明显的问题,在检查收到的 ETH 要不少于售价时 require(msg.value >= priceToPay, "Amount paid is not enough"); 检查的是单个 NFT 的价格,而不是批量购买的总额。而且 ETH 转给卖方的时候用的是合约内的余额。
例如交易合约持有100ETH, 卖家出售其持有的 tokenId 为 1, 2, 3 的 NFT,价格分别为 1ETH, 5ETH, 3ETH。此时买家购买调用 buyMany 应付的价格为 9ETH,但实际上仅需要支付5ETH(购买价最高的 tokenId 的价格)就能满足 require 的检查。此时交易合约共持有105ETH 。转账时,交易合约共向卖方们转了9ETH。即买方购买时少付的 ETH 由交易合约的余额补上了。
在本题中,NFT 的单价为15ETH,因此只需要付15ETH即可购买 6 个NFT,而不是90ETH。但你只有0.5ETH。因此要想办法获得 ETH ,通过 uniswap 的闪电贷就是不错的方法:
- 通过闪电贷借出
15WETH,并将WETH换成ETH - 使用
ETH购买NFT,并将购买的 NFT 发送给FreeRiderBuyer,收到45ETH的奖励。 - 最后将
ETH换回WETH,算上闪电贷的费率还清闪电贷的借款
由于这些操作需要再一笔交易中完成,因此需要写一个攻击合约,不过在此之前需要先看下测试文件 free-rider.challenge.js 做了哪些初始化:
- 给
attacker发送了0.5ETH - 部署合约
WETH、DVT、uniswapFactory、uniswapRouter - 通过调用
uniswapRouter合约的方法addLiquidityETH添加流动性,该方法内部会创建配对合约uniswapPair,配对的币种是WETH-DVT,添加的流动性为9000WETH和15000DVT - 部署合约
FreeRiderNFTMarketplace,并转入90ETH。 - 部署
NFT合约 - 在交易合约中创建卖单出售
tokenId为 0~5 的NFT,每个价格都为15ETH - 部署合约
FreeRiderBuyer并转入45ETH
攻击合约代码如下:
在测试文件 free-rider.challenge.js 添加执行入口
最后执行 yarn free-rider 测试通过!