Dria

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

Malicious User can easily DoS/Grief BuyerAgent purchase

Summary

Consider the function execution of BuyerAgent::purchase

function purchase() external onlyAuthorized {
// check that we are in the Buy phase, and return round
(uint256 round,) = _checkRoundPhase(Phase.Buy);
// check if the task is already processed
uint256 taskId = oraclePurchaseRequests[round];
if (isOracleRequestProcessed[taskId]) {
revert TaskAlreadyProcessed();
}
// read oracle result using the latest task id for this round
bytes memory output = oracleResult(taskId);
address[] memory assets = abi.decode(output, (address[]));
// we purchase each asset returned
for (uint256 i = 0; i < assets.length; i++) {
address asset = assets[i];
// must not exceed the roundly buy-limit
uint256 price = swan.getListingPrice(asset);
spendings[round] += price;
if (spendings[round] > amountPerRound) {
revert BuyLimitExceeded(spendings[round], amountPerRound);
}
// add to inventory
inventory[round].push(asset);
// make the actual purchase
swan.purchase(asset);
}
// update taskId as completed
isOracleRequestProcessed[taskId] = true;
}

The key thing to note is that the success of calling the entire transaction depends on the success of each individual purchase.

Consider what happens when you call `Swan::purchase'.

SwanAsset(_asset).transferFrom(listing.seller, address(this), 1);
SwanAsset(_asset).transferFrom(address(this), listing.buyer, 1);

This point in the process of performing the purchase function is a key place. Asset transfers from listing.seller to buyer.

The key assumption of the protocol here is that the asset belongs to listing.seller (ie to the one who called the ' Swan::list'

However, it is obvious that the asset may not belong to listing.seller, at least listing.seller can intentionally or accidentally just transfer it to another address.

The limitation of this behavior is not provided in the implementation of SwanAsset.

Vulnerability Details

Because the success of the purchase call depends on the success of the purchase of individual assets => it is evident that if such an accidental/intentional user behavior (will send their asset to another address) will DoS the entire purchase function for this round of Buyer Agent.

Of course, it can send a request to the LLM for generating new assets for purchase, but it is not certain that models are sufficiently intelligent to exclude assets by such a principle.

I would say that in 99.99% of the cases the round will never be closed, which means everyone who paid a listing commission in this round will be forced to relist. So they’re just gonna lose money on the commission.

Impact

The impact of this problem is DoS in one round and loss of funds to the listing commission - Severity: Medium

Tools Used

Manual Review

Recommendations

Block all possible asset owner behaviour in AssetManagerrealization. At least override transfer function.

Updates

Lead Judging Commences

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

DoS in BuyerAgent::purchase

Support

FAQs

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