Open code423n4 opened 1 year ago
0xSorryNotSorry marked the issue as high quality report
0xSorryNotSorry marked the issue as duplicate of #752
GalloDaSballo changed the severity to 2 (Med Risk)
GalloDaSballo marked the issue as satisfactory
GalloDaSballo marked the issue as selected for report
Lines of code
https://github.com/code-423n4/2023-04-caviar/blob/main/src/PrivatePool.sol#L268 https://github.com/code-423n4/2023-04-caviar/blob/main/src/EthRouter.sol#L140-L143
Vulnerability details
Impact
Users that submit single or bulk Buy orders through
EthRouter.sol
can have their excess eth stolen by a malicious royalty recipientProof of Concept
Introduction
The
buy(...)
function inPrivatePool.sol
refunds excess ether back toEthRouter.sol
and then pays a royalty amount to a royalty recipient. The order is the following:This turns out to be dangerous since now
buy(...)
inEthRouter.sol
can be reentered from the fallback function of a royalty recipient. In the fallback function the attacker would callbuy
in theEthRouter.sol
with an emptyBuy[] buys calldata
,deadline=0
andpayRoyalties = false
which will skip thefor
loop inbuy(...)
, sincebuys
is empty, and would reach the following block of code:Since now
msg.sender
is the royalty recipient he would receive all the ether that is currently residing inEthRouter.sol
while the originalbuy(...)
triggered by the user hasn't yet finished.Before supplying a PoC implementation in Foundry there are a few caveats to be noted. Firstly, this issue can be more easily reproduced by assuming that the malicious royalty recipient would come either from a single
Buy
order consisting of a singletokenId
or multipleBuy
orders where thetokenId
with the malicious royalty recipient is the lasttokenId
in the array of the lastBuy
order. In the case of thetokenId
associated with the malicious royalty recipient being positioned NOT in last place in thetokenIds[]
array in the lastBuy
order we would have to write afallback
function that after collecting all the ether inEthRouter.sol
somehow extracts information of how much ether would be needed to successfully complete the rest of thebuy(...)
invocations (that will be called on the rest of thetokenIds[]
) and sends that ether back toEthRouter.sol
so that the whole transaction doesn't revert due toEthRouter.sol
being out of funds. In the presented PoC implementation it is assumed thattokenIds
has a single token or the malicious royalty recipient is associated with the lasttokenId
in the lastBuy
if there are multipleBuy
orders. In the case wheretokenId
is positioned not in last place a more sophisticated approach would be needed to steal the excess eth that involves inspecting theEthRouter.buy(...)
while it resides in the transaction mempool and front-running a transaction that configures a fallback() function in the royalty recipient that would send the necessary amount of the stolen excess eth back toEthRouter.sol
so that `buy(...) doesn't revert.PoC implementation
Place the following test in 2023-04-caviar/test.
Run
forge test --ffi --fork-url <polygon-mainnet-rpc-url> --fork-block-number 39900000 -m test_RoyaltyReceiverStealExcessEth -vv
.The expected output in the terminal is:
Note on severity
A severity rating of "high" was chosen due to the following
Although the current state of the nft market mostly has adopted NFTs that have royalty payments directly to the creator, the authors of Caviar have acknowledged the ERC-2981 standard and it is assumed they are aware that
royaltyInfo
returns an arbitrary royalty recipient address.The PoC implementation in this report uses an already existing NFT project - Pixels1024 - deployed on the Polygon network that shows a use case where users are responsible for the creation of a given NFT from a collection and therefore the user-creator is assigned as a royalty recipient.
It is possible that future projects adopting ERC-2981 could have novel and complex interactions between who creates and who receives royalties in a given collection, therefore, extra caution should be a priority when handling
royaltyInfo
requests and the current implementation is shown to have а notable vulnerability.Tools Used
Recommended Mitigation Steps
Rework
buy
inEthRouter.sol
andPrivatePool.sol
. Use reentrancy guard.