-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Lines of code
Vulnerability details
Impact
receiveFlashLoan does not validate the originalCallData,
The attacker can pass any parameters into the receiveFlashLoan function and execute any Strategy instruction :_supplyBorrow _repayAndWithdraw _payDeb.
Proof of Concept
The StrategyLeverage#receiveFlashLoan function only validates whether the msg.sender is _balancerVault, but does not validate the originalCallData:
function receiveFlashLoan(address[] memory tokens,
uint256[] memory amounts, uint256[] memory feeAmounts, bytes memory userData
) external {
@> if (msg.sender != address(_balancerVault)) revert InvalidFlashLoadLender();
if (tokens.length != 1) revert InvalidTokenList();
if (amounts.length != 1) revert InvalidAmountList();
if (feeAmounts.length != 1) revert InvalidFeesAmount();
//@audit originalCallData is not verified
(address borrower, bytes memory originalCallData) = abi.decode(userData, (address, bytes));
address asset = tokens[0];
uint256 amount = amounts[0];
uint256 fee = feeAmounts[0];
// Transfer the loan received to borrower
IERC20(asset).safeTransfer(borrower, amount);
@> if (IERC3156FlashBorrowerUpgradeable(borrower).onFlashLoan(borrower,
tokens[0], amounts[0], feeAmounts[0], originalCallData
) != CALLBACK_SUCCESS
) {
revert BorrowerCallbackFailed();
}
....
}_balancerVault.flashLoan can specify the recipient:
_balancerVault.flashLoan(address(this), tokens, amounts, abi.encode(borrower, data));An attacker can initiate flashLoan from another contract and specify the recipient as BalancerFlashLender. _balancerVault will call the balancerFlashlender#receiveFlashLoan function,
Since the caller of the receiveFlashLoan function is _balancerVault, this can be verified against msg.sender.
The StrategyLeverage#onFlashLoan function parses the instructions to be executed from originalCallData(FlashLoanData) and executes them.
function onFlashLoan(address initiator,address token,uint256 amount, uint256 fee, bytes memory callData
) external returns (bytes32) {
if (msg.sender != flashLenderA()) revert InvalidFlashLoanSender();
if (initiator != address(this)) revert InvalidLoanInitiator();
// Only Allow WETH Flash Loans
if (token != wETHA()) revert InvalidFlashLoanAsset();
//loanAmount = leverage - msg.value;
@> FlashLoanData memory data = abi.decode(callData, (FlashLoanData));
if (data.action == FlashLoanAction.SUPPLY_BOORROW) {
_supplyBorrow(data.originalAmount, amount, fee);
// Use the Borrowed to pay ETH and deleverage
} else if (data.action == FlashLoanAction.PAY_DEBT_WITHDRAW) {
// originalAmount = deltaCollateralInETH
_repayAndWithdraw(data.originalAmount, amount, fee, payable(data.receiver));
} else if (data.action == FlashLoanAction.PAY_DEBT) {
_payDebt(amount, fee);
}
return _SUCCESS_MESSAGE;
}So the attacker by calling the _balancerVault flashLoan function, designated recipient for BalancerFlashLender, borrower for StrategyLeverage,
An attacker can invoke the _supplyBorrow _repayAndWithdraw _payDeb function in StrategyLeverage with any FlashLoanData parameter.
Tools Used
vscode, manual
Recommended Mitigation Steps
BalancerFlashLender#flashLoanfunction to record the parameters called via hash.- Verify the hash value in the
receiveFlashLoanfunction.
Assessed type
Access Control