H-1: Teleport should verify subaccount is tied to current borrower
Summary:
A missing validation could be used to pull funds from dangling authorization.
Description:
The updated functionality of teleport() can be used to migrate a position from a subaccount of the borrower.
237: function teleport(uint toDeposit, uint toBorrow, address subAccount) external onlyBorrowerAndNotExtLiquidated whenNotPaused nonReentrant {
238: createVaultSnapshot();
239:
240: totalAssetsDepositedOrReserved += toDeposit;
241: _handleExcessCredit(_invariantCollateralAmount());
242:
243: if (toBorrow == type(uint).max) {
244: toBorrow = IEVault(targetVault).debtOf(subAccount);
245: }
246:
247: IEVC.BatchItem[] memory items = new IEVC.BatchItem[](3);
248: items[0] = IEVC.BatchItem({
249: targetContract: asset(),
250: onBehalfOfAccount: address(this),
251: value: 0,
252: data: abi.encodeCall(IERC20.transferFrom, (subAccount, address(this), toDeposit)) // needs allowance
253: });
The problem is the absence of a validation that ties the subaccount to the current borrower, leading to a transferFrom() action from an arbitrary account.
Usually, there should not be any active allowance from third-party accounts, as collateral vaults are per borrower. However, since vaults can be liquidated, the borrower may shift to the new liquidator, enabling the new borrower to access the old borrower's funds.
Impact:
High. New vault owners can siphon funds from a previous borrower.
Requirements:
- Collateral vault gets liquidated
- Excess approval exists
- Previous vault owner contains a non-zero amount of the collateral token
- Funds can ONLY be pulled from the previous vault owner
Recommendation:
Validate subaccount is an actual subaccount of the current borrower.
Developer Response:
Fixed in commit 5945b31. To clarify, the proper implementation already existed in the MockCollateralVault contract.