TempleGold

TempleDAO
Foundry
25,000 USDC
View results
Submission Details
Severity: medium
Invalid

Lack of Whitelist Privilege for `SpiceAuction::auctionConfigs.recipient` Will Cause DOS in `SpiceAuction::bid`

Summary

According to the documentation, specific contracts including TempelGoldStaking, DaiGoldAuction, SpiceAuction, and the Gnosis team multisig are intended to be whitelisted using TempleGold::authorizeContract. However, in scenarios where SpiceAuction's auctionConfigs.isTempleGoldAuctionToken is set to false, neither the sender nor the recipient involved in the bidding process is whitelisted. As a result, attempts to execute the bid function will consistently fail due to transfer restrictions, leading to a Denial of Service (DoS) condition for the bidding functionality.

Vulnerability Details

When auctionConfigs.isTempleGoldAuctionToken is set to false, the templeGold token becomes the bid token. The relevant code snippet illustrating this configuration is as follows:

function _getBidAndAuctionTokens(
SpiceAuctionConfig storage _config
) private view returns (address bidToken, address auctionToken) {
@> (bidToken, auctionToken) = _config.isTempleGoldAuctionToken
@> ? (spiceToken, templeGold)
@> : (templeGold, spiceToken);
}

During the execution of the bid function, the contract attempts to transfer bidToken from msg.sender to _recipient. Given that neither party is whitelisted in this configuration, the transfer operation reverts:

function bid(uint256 amount) external virtual override {
/// @dev Cache, gas savings
uint256 epochId = _currentEpochId;
EpochInfo storage info = epochs[epochId];
if (!info.isActive()) {
revert CannotDeposit();
}
if (amount == 0) {
revert CommonEventsAndErrors.ExpectedNonZero();
}
SpiceAuctionConfig storage config = auctionConfigs[epochId];
(address bidToken, ) = _getBidAndAuctionTokens(config);
address _recipient = config.recipient;
uint256 _bidTokenAmountBefore = IERC20(bidToken).balanceOf(_recipient);
@> IERC20(bidToken).safeTransferFrom(msg.sender, _recipient, amount);
uint256 _bidTokenAmountAfter = IERC20(bidToken).balanceOf(_recipient);
// fee on transfer tokens
if (amount != _bidTokenAmountAfter - _bidTokenAmountBefore) {
revert CommonEventsAndErrors.InvalidParam();
}
depositors[msg.sender][epochId] += amount;
info.totalBidTokenAmount += amount;
emit Deposit(msg.sender, epochId, amount);
}

Impact

The absence of whitelist privileges for SpiceAuction::auctionConfigs.recipient when config.isTempleGoldAuctionToken is set to false results in a DoS condition for the bid function. This issue prevents SpiceAuction from functioning correctly in certain configurations, as the bidding process is essential for the auction mechanism to operate. Moreover, adding recipients to the whitelist as a workaround could potentially compromise the non-transferability principle of the templeGold token, indicating a need for a nuanced solution.

Proof of Concept

To demonstrate the issue, consider the following test scenario added to SpiceAuction.t.sol:

function test_BidDOS() public {
uint256 bidTokenAmount = 10 ether;
vm.startPrank(executor);
// create one ProxiedToken with two delegators
// deal templeGold to users to bid
dealAdditional(IERC20(templeGold), alice, bidTokenAmount);
//create auction using proxy address 1 and authorize treasury to bid
//templeGold.authorizeContract(address(treasury), true); //it works if u uncomment this line
// deal AuctionToken ( tokenProxy1)
// set config so that isTempleGoldAuctionToken == false
vm.startPrank(daoExecutor);
ISpiceAuction.SpiceAuctionConfig memory _config = _getAuctionConfig2();
spice.setAuctionConfig(_config);
vm.warp(block.timestamp + _config.waitPeriod);
spice.startAuction();
IAuctionBase.EpochInfo memory info = spice.getEpochInfo(1);
// // auction started
vm.warp(info.startTime + 1);
// alice bids 10 templeGold
vm.startPrank(alice);
templeGold.approve(address(spice), type(uint).max);
vm.expectRevert();
spice.bid(bidTokenAmount);
}

This test case illustrates that under the specified conditions, the bid function will revert due to the lack of whitelist privileges, effectively demonstrating the DoS vulnerability.

Tools Used

Manual Review

Recommendations

To mitigate the identified vulnerability and ensure the SpiceAuction::bid function operates as intended without causing a Denial of Service (DoS), consider the following approaches:
Option 1: Whitelisting all _recipients

This could involve temporarily authorizing the _recipient address for the duration of the auction or until the bid is processed. Careful implementation is required to ensure that this whitelisting does not inadvertently compromise the non-transferability of the templeGold token outside of legitimate auction activities.

Option 2: Two-Step Transfer Process

Modify the bid function to incorporate a two-step transfer process specifically for scenarios where config.isTempleGoldAuctionToken is set to false. First, transfer the bid tokens from msg.sender to the SpiceAuction contract itself. Then, transfer the tokens from the contract to _recipient. This approach circumvents the need for the recipient to be whitelisted, as the contract is assumed to be authorized for token transfers.

Here's an illustrative code adjustment for the bid function:

function bid(uint256 amount) external virtual override {
/// @dev Cache, gas savings
uint256 epochId = _currentEpochId;
EpochInfo storage info = epochs[epochId];
if (!info.isActive()) {
revert CannotDeposit();
}
if (amount == 0) {
revert CommonEventsAndErrors.ExpectedNonZero();
}
SpiceAuctionConfig storage config = auctionConfigs[epochId];
(address bidToken, ) = _getBidAndAuctionTokens(config);
address _recipient = config.recipient;
uint256 _bidTokenAmountBefore = IERC20(bidToken).balanceOf(_recipient);
- IERC20(bidToken).safeTransferFrom(msg.sender, _recipient, amount);
+ if (bidToken == templeGold){
+ IERC20(bidToken).safeTransferFrom(msg.sender, address(this), amount);
+ IERC20(bidToken).safeTransfer(_recipient,amount);
+ } else{
+ IERC20(bidToken).safeTransferFrom(msg.sender, _recipient, amount);
+ }
uint256 _bidTokenAmountAfter = IERC20(bidToken).balanceOf(_recipient);
// fee on transfer tokens
if (amount != _bidTokenAmountAfter - _bidTokenAmountBefore) {
revert CommonEventsAndErrors.InvalidParam();
}
depositors[msg.sender][epochId] += amount;
info.totalBidTokenAmount += amount;
emit Deposit(msg.sender, epochId, amount);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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