参考:openzepplin的ERC20FlashMintERC3156是整个FlashLoan的标准,FlashMint只是其中一个特例。FlashLoan主要的可能漏洞是:1、通过在flashloan借贷内部质押(deposit)之类的,来替代repay,达到攻击目的2、通过
参考:openzepplin 的ERC20FlashMint
ERC3156 是整个FlashLoan的标准,FlashMint只是其中一个特例。 FlashLoan主要的可能漏洞是:
1、通过在flashloan借贷内部质押(deposit)之类的,来替代repay,达到攻击目的
2、通过借贷的Token去dex操纵价格,因为这里可以借用的token是可以无限大的(没有设置最大值的话)
FUNCTIONS
默认fee是为0 ,feeReceiver是address(0)
**我们来看看最近审计一个项目的代码----大家仔细看看代码,能看出问题在哪吗?
/// @notice Performs a flash mint (called flash loan to confirm with ERC3156 standard).
///
/// @param receiver The address which will receive the flash minted tokens.
/// @param token The address of the token to flash mint.
/// @param amount How much to flash mint.
/// @param data ABI encoded data to pass to the receiver.
///
/// @return If the flash loan was successful.
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytes calldata data
) external override nonReentrant returns (bool) {
if (token != address(this)) {
revert IllegalArgument();
}
if (amount > maxFlashLoan(token)) {
revert IllegalArgument();
}
uint256 fee = flashFee(token, amount);
_mint(address(receiver), amount);
if (receiver.onFlashLoan(msg.sender, token, amount, fee, data) != CALLBACK_SUCCESS) {
revert IllegalState();
}
_burn(address(receiver), amount + fee); // Will throw error if not enough to burn
return true;
}
我们可以对比openzepplin的实现
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytes calldata data
) public virtual override returns (bool) {
require(amount <= maxFlashLoan(token), "ERC20FlashMint: amount exceeds maxFlashLoan");
uint256 fee = flashFee(token, amount);
_mint(address(receiver), amount);
require(
receiver.onFlashLoan(msg.sender, token, amount, fee, data) == _RETURN_VALUE,
"ERC20FlashMint: invalid return value"
);
address flashFeeReceiver = _flashFeeReceiver();
_spendAllowance(address(receiver), address(this), amount + fee);
if (fee == 0 || flashFeeReceiver == address(0)) {
_burn(address(receiver), amount + fee);
} else {
_burn(address(receiver), amount);
_transfer(address(receiver), flashFeeReceiver, fee);
}
return true;
}
发现问题代码的实现少了关键的一行:
_spendAllowance(address(receiver), address(this), amount + fee);
缺少这一行会导致什么问题呢
如果一个合约是IERC3156FlashBorrower receiver,而且合约上面有资金,那么任意的地址都可以通过flashMint来消耗上面的资金即消耗fee——损人不利己的事情。
有这一行代码,保证了其他的任意调用不会成功。你在真正需要flashmint的时候,在回调里面会添加approve()授权函数来保证,销毁成功。
注意点:
1、FlashLoan在借贷的回调里面是,将token转会Loaner合约,而在FlashMint的回调里面是,将token授权给Token合约,让它可以销毁
2、token授权给Token合约这个事情有点点感觉没那么自然,但是是必须的。需要注意。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!