Swan.sol contract does not verify if the BuyerAgent origin is indeed BuyerAgentFactory which was deployed buy the original team. Because of this malicious buyerAgents can be passed as parameters to both list()/relist() functions.
Malicious BuyerAgent can circumvent default limits placed on royaltyFee and set it to whatever they desire. Because of this:
The conditions for the exploit to work are:
seller has to pass malicious buyer as param
seller has to have allowance set for Swan beyond the price passed
If these are satisfied the exploit will steal tokens from seller. A piece of stolen tokens will also be passed to the Swan owner as driaFee.
The BuyerAgent royaltyLimit() limit circumvention can happen in many ways as the contract can be freely modified. Some of them are:
modifying royaltyFee() function
removing setRoyaltyFee() limits checks
removing limits from the constructor
...
The malicious BuyerAgent can try making better educated guesses by checking the seller allowances and balance and looking for large sums. But in the end it would still be a guess, because there is no way for the BuyerAgent at being called at royaltyFee() function to know what the set price is going to be:
The guess can be somewhat improved if it is called from relist() because the initial price will be set, but the malicious actor can't be sure it's not going to be changed to a bigger value (and therefor failing the transaction after inflated royaltyFee grows the buyerFee amount beyond sellers balance).
Malicious buyer agent can also use make royaltyFee() initially return an expected amount (if seller tries to check the fee explicitly). And only change it's value once royaltyFee() is called inside list() simply by checking who the msg.sender is.
Because, swan calls old buyerAgent before relisting to the new one. The buyerAgent can decide to revert all functions, this way the asset can be held hostage. By neither finalizing with purchase() nor allowing to relist to other buyerAgents.
Greatly exaggerated buyerFee will steal tokens from seller and still set the price as it was supposed to. And malicious buyer can hold listed assets hostage. Medium - because the sellers has to pass the malicious address by themselves and also depends if malicious actor can add his address to the app UI to gain visibility.
Manual review + foundry tests
Adding re-written hardhat PoC:
MaliciousBuyerAgent only change from BuyerAgent is the removed royaltyLimit check in the constructor:
Add a check to see that the BuyerAgent was indeed deployed buy the BuyerAgent factory. Reject any other addresses.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.