DittoETH

Ditto
DeFiFoundryOracle
55,000 USDC
View results
Submission Details
Severity: medium
Invalid

Unbounded loop & lack of duplicate-array-element check in external function `distributeYield()` exposes protocol to DoS attack vector

Summary

An attacker can launch a DoS attack by calling the distributeYield() function with a huge array of address[] calldata assets, making the protocol service unavailable for other users.

Vulnerability Details

distributeYield() allows the array assets to be passed as a param with no limit on its length. Also, duplicate values inside the array are allowed.

File: contracts/facets/YieldFacet.sol
53 function distributeYield(address[] calldata assets) external nonReentrant {
54 uint256 length = assets.length;
55 uint256 vault = s.asset[assets[0]].vault;
56
57 // distribute yield for the first order book
58 (uint88 yield, uint256 dittoYieldShares) = _distributeYield(assets[0]);
59
60 // distribute yield for remaining order books
61 for (uint256 i = 1; i < length;) {
62 if (s.asset[assets[i]].vault != vault) revert Errors.DifferentVaults();
63 (uint88 amtYield, uint256 amtDittoYieldShares) = _distributeYield(assets[i]);
64 yield += amtYield;
65 dittoYieldShares += amtDittoYieldShares;
66 unchecked {
67 ++i;
68 }
69 }
70 // claim all distributed yield
71 _claimYield(vault, yield, dittoYieldShares);
72 emit Events.DistributeYield(vault, msg.sender, yield, dittoYieldShares);
73 }

So, an attacker can create a huge array with a single asset address at every index and pass it to the function. This causes a DoS attack and can cause the protocol service to be unavailable to other users. Note that there is nothing to be gained in terms of yield by passing an array with duplicate asset addresses, so this would be done only for a DoS attack.


Add the following test inside test/Yield.t.sol and run it through forge test --mt test_DistributeYield_HugeDuplicateAssetArray -vv.

function test_DistributeYield_HugeDuplicateAssetArray() public {
fundLimitShortOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, receiver);
fundLimitBidOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, sender);
skip(yieldEligibleTime);
generateYield(1 ether);
// @audit-info : example shown with array of size 4, but an
// attacker can create a huge array of duplicate entries here.
address[] memory assets = new address[](4);
assets[0] = asset;
assets[1] = asset;
assets[2] = asset;
assets[3] = asset;
uint256 ethEscrowed1 = diamond.getVaultUserStruct(vault, receiver).ethEscrowed;
vm.prank(receiver);
diamond.distributeYield(assets);
uint256 ethEscrowed2 = diamond.getVaultUserStruct(vault, receiver).ethEscrowed;
assertApproxEqAbs(ethEscrowed2 - ethEscrowed1, 900000000000000000, MAX_DELTA);
}

Impact

DoS attack makes the protocol service unavailable for other users.

Tools Used

Manual inspection and foundry.

Recommendations

A constraint like the following can be added in distributeYield() which puts an upper limit on the assets array size being passed into the function. If a user really needs to pass many assets, he can try in separate transactions.

53 function distributeYield(address[] calldata assets) external nonReentrant {
54 uint256 length = assets.length;
+ require(length < 10, "Too many assets");
55 uint256 vault = s.asset[assets[0]].vault;
56
57 // distribute yield for the first order book
58 (uint88 yield, uint256 dittoYieldShares) = _distributeYield(assets[0]);
59
60 // distribute yield for remaining order books
61 for (uint256 i = 1; i < length;) {
62 if (s.asset[assets[i]].vault != vault) revert Errors.DifferentVaults();
63 (uint88 amtYield, uint256 amtDittoYieldShares) = _distributeYield(assets[i]);
64 yield += amtYield;
65 dittoYieldShares += amtDittoYieldShares;
66 unchecked {
67 ++i;
68 }
69 }
70 // claim all distributed yield
71 _claimYield(vault, yield, dittoYieldShares);
72 emit Events.DistributeYield(vault, msg.sender, yield, dittoYieldShares);
73 }
Updates

Lead Judging Commences

0xnevi Lead Judge
about 2 years ago
0xnevi Lead Judge about 2 years ago
Submission Judgement Published
Invalidated
Reason: Out of Gas

Support

FAQs

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