合约代码:
RewardsHypervisor:https://etherscan.io/address/0xC9f27A50f82571C1C8423A42970613b8dBDA14ef#code
攻击交易:https://etherscan.io/tx/0x69272d8c84d67d1da2f6425b339192fa472898dce936f24818fda415c1c1ff3f
合约RewardsHypervisor
中函数deposit
function deposit(
uint256 visrDeposit,
address payable from,
address to
) external returns (uint256 shares) {
///未防止重入
require(visrDeposit > 0, "deposits must be nonzero");
require(to != address(0) && to != address(this), "to");
require(from != address(0) && from != address(this), "from");
shares = visrDeposit;
if (vvisr.totalSupply() != 0) {
uint256 visrBalance = visr.balanceOf(address(this));
shares = shares.mul(vvisr.totalSupply()).div(visrBalance);
}
if(isContract(from)) {
require(IVisor(from).owner() == msg.sender);
IVisor(from).delegatedTransferERC20(address(visr), address(this), visrDeposit); ///可以用来重入/ 也可以用来绕过代币Transfer直接白嫖share
}
else {
visr.safeTransferFrom(from, address(this), visrDeposit);
}
vvisr.mint(to, shares);
}
再调用deposit
函数时 如果from为合约地址,同时from.owner()为调用者时。就会调用from.delegatedTransferERC20
而from为用户传入只需要在合约中编辑owner函数代码即可。
因为deposit
中没有校验是否真的传入token。所以visrDeposit的值可以随便填写。
攻击者使用重入进行攻击,嗯…