Dria

Swan
NFTHardhat
21,000 USDC
View results
Submission Details
Severity: low
Invalid

Missing Threshold for Interval Values. Protocol is Vulnerable to DoS Attack

Summary

In this protocol, intervals regulate transitions in round, roundTime, and phases, controlling crucial functions such as selling, withdrawing, etc. However, the current implementation lacks validation for these interval values, exposing the protocol to potential Denial of Service (DoS) vulnerabilities. The following code snippet demonstrates this issue, where intervals can be set to zero without restriction:

/// @notice Adds new market parameters to the marketParameters array.
/// @dev Restricted to the owner.
/// @param _marketParameters New market configuration
function setMarketParameters(SwanMarketParameters memory _marketParameters) external onlyOwner {
require(_marketParameters.platformFee <= 100, "Platform fee cannot exceed 100%");
_marketParameters.timestamp = block.timestamp;
@> // Warning: Minimum interval thresholds not enforced
marketParameters.push(_marketParameters);
}

If intervals are set to zero, a DoS vulnerability is triggered as the protocol attempts to compute the phase, round, and cycleTime, which all depend on valid interval values.

function _computeCycleTime(SwanMarketParameters memory params) internal pure returns (uint256) {
// Warning: Possible zero values
@> return params.sellInterval + params.buyInterval + params.withdrawInterval;
}
function _computePhase(SwanMarketParameters memory params, uint256 elapsedTime)
internal
pure
returns (uint256, Phase, uint256)
{
// Warning: `cycleTime` might be zero
@> uint256 cycleTime = _computeCycleTime(params);
uint256 round = elapsedTime / cycleTime; // Undefined if cycleTime = 0
uint256 roundTime = elapsedTime % cycleTime;
if (roundTime <= params.sellInterval) {
return (round, Phase.Sell, params.sellInterval - roundTime);
} else if (roundTime <= params.sellInterval + params.buyInterval) {
return (round, Phase.Buy, params.sellInterval + params.buyInterval - roundTime);
} else {
return (round, Phase.Withdraw, cycleTime - roundTime);
}
}

Vulnerability Details

The following test code provides both proof of code and proof of concept, demonstrating the protocol's susceptibility to this vulnerability. Add this code to Swan.test.ts under the Swan test suite.

describe("Swan Attack Mode", async () => {
const currRound = 0n;
it("should list 5 assets for the first round", async function () {
await listAssets(
swan,
buyerAgent,
[
[seller, PRICE1],
[seller, PRICE2],
[seller, PRICE3],
[sellerToRelist, PRICE2],
[sellerToRelist, PRICE1],
],
NAME,
SYMBOL,
DESC,
0n
);
[assetToBuy, assetToRelist, assetToFail, ,] = await swan.getListedAssets(await buyerAgent.getAddress(), currRound);
expect(await token.balanceOf(seller)).to.be.equal(FEE_AMOUNT1 + FEE_AMOUNT2);
expect(await token.balanceOf(sellerToRelist)).to.be.equal(0);
});
it("should NOT allow listing of assets beyond maximum asset count", async function () {
await expect(swan.connect(sellerToRelist).list(NAME, SYMBOL, DESC, PRICE1, await buyerAgent.getAddress()))
.to.be.revertedWithCustomError(swan, "AssetLimitExceeded")
.withArgs(MARKET_PARAMETERS.maxAssetCount);
});
it("0 intervals trigger DoS due to undefined cycleTime and round computation", async () => {
let NEW_MARKET_PARAMETERS = {
withdrawInterval: minutes(0),
sellInterval: minutes(0),
buyInterval: minutes(0),
platformFee: 2n,
maxAssetCount: 2n,
timestamp: (await ethers.provider.getBlock("latest")).timestamp,
};
await swan.connect(dria).setMarketParameters(NEW_MARKET_PARAMETERS);
await expect(swan.connect(seller).list(NAME, SYMBOL, DESC, PRICE1, await buyerAgent.getAddress())).to.be.reverted;
});
});

Steps to Run Test

  1. Comment out all subsequent tests in Swan after Sell phase #1: listing inclusively.

  2. Execute the test with the following command:

    yarn test --grep "Swan"
  3. Review the output logs, which should indicate the successful identification of the vulnerability.

Swan
should deploy swan (709ms)
should create buyers (122ms)
should fund buyers & sellers (61ms)
✔ should register oracles
Swan Attack Mode
✔ should list 5 assets for the first round (55ms)
should NOT allow listing beyond max asset count (54ms)
0 intervals trigger DoS due to undefined round computation! (49ms)

As seen, the test succeeds in exposing the vulnerability.

Impact

  • Disrupts core protocol functionality.

  • Restricts users from listing and re-listing assets.

  • Zero intervals alter protocol behavior, potentially leading to malicious operation.

Recommendations

To mitigate this vulnerability, validate interval values to prevent them from being set to zero. The following modification ensures these values are properly constrained:

function setMarketParameters(SwanMarketParameters memory _marketParameters) external onlyOwner {
require(_marketParameters.platformFee <= 100, "Platform fee cannot exceed 100%");
_marketParameters.timestamp = block.timestamp;
+ require(_marketParameters.buyInterval > 0 && _marketParameters.sellInterval > 0 && _marketParameters withdrawInterval > 0, "Interval values cannot be zero");
marketParameters.push(_marketParameters);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 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.