Relevant GitHub Links
https://github.com/Cyfrin/2024-07-templegold/blob/main/protocol/contracts/templegold/DaiGoldAuction.sol#L260
Summary
The minted templeGold is strictly restricted to be used in stake, escrow, and team gnosis contracts. However, the recoverToken function in the DaiGoldAuction.sol escrow contract allows privileged users to transfer auction tokens directly, bypassing the auction process. This function is intended as a remedy for misconfigured auction settings, but it poses a risk to the protocol's funds and raises concerns about the protocol's security among users.
Vulnerability Details
At Line 260, the protocol allows privileged users to transfer templeGold intended for auction directly, without restrictions on the destination address. Given the relatively lenient access control over this function, there is a risk of granting permissions to inappropriate parties, potentially leading to fund misappropriation.
function recoverToken(
address token,
address to,
uint256 amount
@> ) external override onlyElevatedAccess {
...
uint256 epochId = _currentEpochId;
EpochInfo storage info = epochs[epochId];
if (info.startTime == 0) { revert InvalidOperation(); }
if (info.isActive()) { revert AuctionActive(); }
if (info.hasEnded()) { revert AuctionEnded(); }
@> uint256 _totalAuctionTokenAmount = info.totalAuctionTokenAmount;
@> if (amount > _totalAuctionTokenAmount) { revert CommonEventsAndErrors.InvalidAmount(token, amount); }
delete epochs[epochId];
unchecked {
@> nextAuctionGoldAmount += _totalAuctionTokenAmount - amount;
}
emit CommonEventsAndErrors.TokenRecovered(to, token, amount);
@> templeGold.safeTransfer(to, amount);
}
POC
Place the following test into DaiGoldAuction.t.sol::DaiGoldAuctionTest:
function test_recoverTokenToAttacker() public {
_setVestingFactor(templeGold);
skip(1 days);
vm.startPrank(executor);
templeGold.mint();
uint256 mintAmount = templeGold.balanceOf(address(daiGoldAuction));
IDaiGoldAuction.AuctionConfig memory _config = _getAuctionConfig();
daiGoldAuction.setAuctionConfig(_config);
_startAuction();
IAuctionBase.EpochInfo memory info = daiGoldAuction.getEpochInfo(1);
vm.warp(info.startTime - 10 seconds);
address attacker = makeAddr("attack");
uint256 _beforeRecoverBalance = templeGold.balanceOf(attacker);
assertEq(_beforeRecoverBalance, 0);
vm.startPrank(executor);
daiGoldAuction.recoverToken(address(templeGold), attacker, mintAmount);
uint256 _afterRecoverBalance = templeGold.balanceOf(attacker);
assertEq(_afterRecoverBalance, mintAmount);
}
Impact
For the project team, there's a risk of fund loss if recovery permissions are granted to the wrong individuals.
For users, the project team has the ability to bypass the strict issuance constraints of templeGold, making its value unpredictable and casting doubt on the long-term value of the project, which could reduce user engagement.
Tools Used
Manual Review.
Recommendations
Impose Stricter Restrictions on the Method: Ensure that this function can only be used under specific conditions such as during rescueMode or by a designated executor.
Restrict to Address when Handling templeGold: Validate the to address to ensure it is a multisignature wallet, thus limiting the flow of these templeGold tokens to trusted destinations.
function recoverToken(
address token,
address to,
uint256 amount
) external override onlyElevatedAccess {
...
+ // Add checks to ensure 'to' address is among trusted addresses
+ require(isTrustedAddress(to), "Invalid recovery address");
// auction started but cooldown pending
uint256 epochId = _currentEpochId;
...
emit CommonEventsAndErrors.TokenRecovered(to, token, amount);
templeGold.safeTransfer(to, amount);
}
+ // Example function to check trusted addresses
+ function isTrustedAddress(address _address) internal view returns (bool) {
+ return _address == trustedRecoveryAddress || _address == otherTrustedAddress;
+ }
By implementing these recommendations, the protocol can significantly mitigate the potential misuse of the recoverToken function and enhance the security and trust in the system.