HardhatDeFi
15,000 USDC
View results
Submission Details
Severity: medium
Invalid

Malicious Block-listing in Batch Operations Enables Mass Gas Consumption Attack and Protocol DOS

Summary

The batch operations in AaveDIVAWrapper are vulnerable to denial of service attacks when using tokens with blocklist functionality (like USDC, USDT) due to the ability of malicious users to get themselves blocklisted mid-batch.

https://github.com/Cyfrin/2025-01-diva/blob/main/contracts/src/AaveDIVAWrapper.sol#L126

function batchAddLiquidity(AddLiquidityArgs[] calldata _addLiquidityArgs) external nonReentrant {
uint256 _length = _addLiquidityArgs.length;
for (uint256 i = 0; i < _length; i++) {
// If any user is blocklisted, entire batch fails
_addLiquidity(
_addLiquidityArgs[i].poolId,
_addLiquidityArgs[i].collateralAmount,
_addLiquidityArgs[i].longRecipient,
_addLiquidityArgs[i].shortRecipient
);
}
}
function _handleTokenOperations(address _collateralToken, uint256 _collateralAmount, address _wToken) private {
// Will revert if user is blocklisted
IERC20Metadata(_collateralToken).safeTransferFrom(msg.sender, address(this), _collateralAmount);
...
}

Attack Steps:

  1. Attacker participates in batch operation

  2. Gets themselves blocklisted (e.g., by violating token terms)

  3. When their operation executes, transferFrom reverts

  4. Entire batch fails, wasting gas for all participants

Impact

The batch operation vulnerability enables a targeted DOS attack through user-controlled blocklist status. A malicious user can deliberately trigger their own blocklisting, causing safeTransferFrom to revert and invalidating all operations in the batch. This creates a disproportionate impact where a single actor can grief attack multiple legitimate users and disrupt protocol operations.

Since the vulnerability affects major stablecoins like USDC/USDT, it renders batch functionality effectively unusable for high-volume operations, as any participant can maliciously force batch failures. The attack requires minimal resources from the attacker while imposing maximum costs on other participants, making it an attractive griefing vector.

Recommended Fix

Implement try/catch per operation to allow batch to continue if individual operations fail:

function batchAddLiquidity(AddLiquidityArgs[] calldata _addLiquidityArgs) external nonReentrant {
uint256 _length = _addLiquidityArgs.length;
// Track successes and failures
bool[] memory successes = new bool[]();
bytes[] memory results = new bytes[]();
for (uint256 i = 0; i < _length; i++) {
try this._addLiquidity(
_addLiquidityArgs[i].poolId,
_addLiquidityArgs[i].collateralAmount,
_addLiquidityArgs[i].longRecipient,
_addLiquidityArgs[i].shortRecipient
) {
successes[i] = true;
} catch (bytes memory err) {
successes[i] = false;
results[i] = err;
}
}
emit BatchOperationResults(successes, results);
}
Updates

Lead Judging Commences

bube Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!