Closed sherlock-admin3 closed 7 months ago
Escalate Report states: "Before settling the auction via the call to _closeAuction(uint256), the incumbent steward transfers the token to another incumbent-controlled address, 0xdeadbeef." However during the auction token transfer is locked and this vulnerability is not possible. Token transfer before an auction makes this way too unlikely and this should be invalidated.
Escalate Report states: "Before settling the auction via the call to _closeAuction(uint256), the incumbent steward transfers the token to another incumbent-controlled address, 0xdeadbeef." However during the auction token transfer is locked and this vulnerability is not possible. Token transfer before an auction makes this way too unlikely and this should be invalidated.
The escalation could not be created because you are not exceeding the escalation threshold.
You can view the required number of additional valid issues/judging contest payouts in your Profile page, in the Sherlock webapp.
Escalate
Invalid. The transfer isn't possible as _beforeTokenTransfer()
locks the token after the auction start.
Escalate
Invalid. The transfer isn't possible as
_beforeTokenTransfer()
locks the token after the auction start.
You've created a valid escalation!
To remove the escalation from consideration: Delete your comment.
You may delete or edit your escalation comment anytime before the 48-hour escalation window closes. After that, the escalation becomes final.
@Aamirusmani1552 @utkuerkin
The token is eligible for transfer after the auction has ended.
The issue states that the malicious auction winner transfers the token to an address under their control prior to closing out the finished auction, not whilst the auction is ongoing.
@Aamirusmani1552 @utkuerkin
The token is eligible for transfer after the auction has ended.
The issue states that the malicious auction winner transfers the token to an address under their control prior to closing out the finished auction, not whilst the auction is ongoing.
Still don't get the idea about how malicious auction winner could transfer the token after auction has ended. The isAuctionPeriod
check inside _beforeTokenTransfer()
is all about block.timestamp >= _auctionStartTime(tokenId)
, while the way to change
the return of _auctionStartTime
is to call _closeAuction
.
So how could malicious auction winner transfer the token before call _closeAuction
?
The token is eligible for transfer after the auction has ended.
I don't think this is true. So if I understand correctly, you are saying we can transfer the token after the auction has ended and before the closeAuction
is called.
In order for token transfer the take place, isAuctionPeriod
should be false. Code:
bool isAuctionPeriod = IPeriodicAuctionReadable(address(this))
.isAuctionPeriod(tokenId);
require(
!isAuctionPeriod,
'StewardLicenseFacet: Cannot transfer during auction period'
);
Lets see the condition for isAuctionPeriod() to return false. Code:
return block.timestamp >= _auctionStartTime(tokenId);
Above expression only returns false if _auctionStartTime
is greater than block.timestamp. Lets see how _auctionStartTime
can be greater than block.timestamp. Below is how the auctionStartTime is calculated. Code:
auctionStartTime = l.lastPeriodEndTime[tokenId] + l.currentLicensePeriod[tokenId];
In order for auctionStartTime
to be greater than block.timestamp, the token should be in the license period. However, if the token is in license period, we cannot place a bid. So it's not possible to transfer the token before you call closeAuction
.
The token is eligible for transfer after the auction has ended.
I don't think this is true. So if I understand correctly, you are saying we can transfer the token after the auction has ended and before the
closeAuction
is called.In order for token transfer the take place,
isAuctionPeriod
should be false. Code:bool isAuctionPeriod = IPeriodicAuctionReadable(address(this)) .isAuctionPeriod(tokenId); require( !isAuctionPeriod, 'StewardLicenseFacet: Cannot transfer during auction period' );
Lets see the condition for isAuctionPeriod() to return false. Code:
return block.timestamp >= _auctionStartTime(tokenId);
Above expression only returns false if
_auctionStartTime
is greater than block.timestamp. Lets see how_auctionStartTime
can be greater than block.timestamp. Below is how the auctionStartTime is calculated. Code:auctionStartTime = l.lastPeriodEndTime[tokenId] + l.currentLicensePeriod[tokenId];
In order for
auctionStartTime
to be greater than block.timestamp, the token should be in the license period. However, if the token is in license period, we cannot place a bid. So it's not possible to transfer the token before you callcloseAuction
.
Agreed, I don't think this issue is valid. Back to writing PoCs I think. 😜
I believe #33 should be de-duplicated from this issue as it does describe a valid edge case where a token can be transferred during auction.
Yeah looks like this one is a non-issue. _beforeTokenTransfer hook prevents transfer of the NFT anytime after the auction has started; and the next auction start time is set when the auction is closed.
Yup #33 can be de-duplicated.
Considering the consensus among sponsors to remove this issue’s association with #33, and with the watson’s concurrence on its invalidation, it stands to reason that this issue should be deemed invalid.
Planning to invalidate this issue and duplicates, without #33, which will be considered separately.
Result: Invalid Unique
cawfree
high
The incumbent steward can avoid paying the periodic honorarium.
Summary
Incumbent stewards, as the current owner of the token under auction, are subject to a unique auction mechanism where the collateral value of their placed bids contribute only towards paying the periodic honorarium, as this eliminates the admittedly redundant process of paying themselves.
However, this execution path can be manipulated by the steward by transferring the token in the time period between winning the auction and settling it, enabling the incumbent steward to win auctions risk free and even turn an attacker-controlled profit in proportion to the periodic honorarium.
Vulnerability Detail
Firstly, we need to emphasise that whenever an incumbent steward makes a bid via
placeBid(uint256,address,uint256,uint256)
, they must specify a value ofbidAmount
andcollateralAmount
which will ensure that a precisefeeAmount
is paid.Let's quickly run through how this works.
For an incumbent steward, their computed
feeAmount
is hardcoded to thetotalCollateralAmount
:To ensure the transaction doesn't revert with
EnglishPeriodicAuction: Incorrect bid amount
, the incumbent steward call is expected provide an equivalentbidAmount
for the collateral value that will guarantee the call to_checkBidAmount(uint256, uint256)
will satisfy the following strict equality check:Here, the
feeAmount
(which we now know is hard-wired to thetotalCollateralAmount
) must equal the result of running_calculateFeeFromBid(uint256)
on the caller-suppliedbidAmount
value:For any auction which intends on collecting fees on behalf of the creator circle*, the
bidAmount
specified by the caller must be artificially inflated relative to thecollateralAmount
in order for the strict equality check to be satisfied.The Exploit
With that initial context out of the way, let's get down to the main issue.
Let's assume that the incumbent steward intends to submit a
collateralAmount
of1 ether
and the periodic honorarium is configured to 10%. This will necessitate a correspondingbidAmount
of1.1 ether
, and save aBid
as follows:Let's fast forward to the end of the auction, and the incumbent steward is the winner.
Before settling the auction via the call to
_closeAuction(uint256)
, the incumbent steward transfers the token to another incumbent-controlled address,0xdeadbeef
.Let's step through what happens.
oldBidder
variable is assigned to0xdeadbeef
, and not the incumbent steward themselves.0xdeadbeef
, we execute the following logic:0xdeadbeef
:feeAmount
to the beneficiary - that's right, everyone gets paid! 🤑The incumbent steward gets to hold onto their token, and make a profit of
0.1 ether
for the trouble.Impact
This exploit enables the incumbent steward to permanently hold the asset risk-free and make profit on the asset in proportion to the periodic honorarium fee on their
totalCollateralAmount
, an attacker-controlled unit, at the expense of honest bidders.High.
Code Snippet
📄 EnglishPeriodicAuctionInternal.sol
Tool used
Manual Review
Recommendation
Don't give the incumbent steward a privileged path of execution.