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

`AaveDIVAWrapper::batchApproveCollateralTokenForAave` & `AaveDIVAWrapper::approveCollateralTokenForAave` Lacks Access Controls Allowing Unauthorized Approvals

Summary

AaveDIVAWrapper::batchApproveCollateralTokenForAave & AaveDIVAWrapper::approveCollateralTokenForAave Lacks Access Controls Allowing Unauthorized Approvals

Vulnerability Details

The AaveDIVAWrapper::batchApproveCollateralTokenForAave & AaveDIVAWrapper::approveCollateralTokenForAave functions have no access controls, allowing any user to approve registered collateral tokens for Aave. These functions can be called repeatedly by malicious actors to manipulate approval states or cause unnecessary gas costs.

function batchApproveCollateralTokenForAave(address[] calldata _collateralTokens) external override {
uint256 _length = _collateralTokens.length;
for (uint256 i = 0; i < _length; i++) {
_approveCollateralTokenForAave(_collateralTokens[i]);
}
}
function _approveCollateralTokenForAave(address _collateralToken) internal {
if (AaveDIVAWrapper::_collateralTokenToWToken[_collateralToken] == address(0)) {
revert CollateralTokenNotRegistered();
}
uint256 currentAllowance = IERC20Metadata(_collateralToken).allowance(address(this), AaveDIVAWrapper::_aaveV3Pool);
IERC20Metadata(_collateralToken).safeIncreaseAllowance(AaveDIVAWrapper::_aaveV3Pool, type(uint256).max - currentAllowance);
}

Proof of Concept

describe("Unauthorized Approval", function() {
it("Should allow any address to call approval function", async function() {
const { s } = await loadFixture(setup);
const maliciousActor = s.acc2;
await s.aaveDIVAWrapper
.connect(s.owner)
.registerCollateralToken(collateralToken);
// Malicious actor can repeatedly call approval
await s.aaveDIVAWrapper
.connect(maliciousActor)
.batchApproveCollateralTokenForAave([collateralToken]);
// Can call multiple times without restriction
await s.aaveDIVAWrapper
.connect(maliciousActor)
.batchApproveCollateralTokenForAave([collateralToken]);
});
});

Impact

  • Malicious actors can repeatedly call approvals causing unnecessary gas costs for protocol

  • Potential for approval manipulation if tokens require approval reset

  • External control over protocol approval states could interfere with protocol operations

Tools Used

Hardhat

Recommendations

Two possible approaches:

  1. Restrictive approach - Add onlyOwner modifier:

- function batchApproveCollateralTokenForAave(address[] calldata _collateralTokens) external override {
+ function batchApproveCollateralTokenForAave(address[] calldata _collateralTokens) external override onlyOwner {
uint256 _length = _collateralTokens.length;
for (uint256 i = 0; i < _length; i++) {
_approveCollateralTokenForAave(_collateralTokens[i]);
}
}
  1. Rate-limited approach - Keep permissionless but add controls:

+ mapping(address => uint256) public lastApprovalTimestamp;
function batchApproveCollateralTokenForAave(address[] calldata _collateralTokens) external override {
+ require(block.timestamp >= lastApprovalTimestamp[msg.sender] + 1 days, "Rate limit: wait 24h");
+ lastApprovalTimestamp[msg.sender] = block.timestamp;
+ emit CollateralTokenApprovalUpdated(msg.sender, _collateralTokens);
uint256 _length = _collateralTokens.length;
for (uint256 i = 0; i < _length; i++) {
_approveCollateralTokenForAave(_collateralTokens[i]);
}
}
+ event CollateralTokenApprovalUpdated(address indexed caller, address[] tokens);

The rate-limited approach is recommended as it preserves needed functionality while preventing abuse. This keeps the system permissionless but controlled.

Updates

Lead Judging Commences

bube Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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