Platform: Code4rena
Start Date: 07/07/2023
Pot Size: $121,650 USDC
Total HM: 36
Participants: 111
Period: 7 days
Judge: Picodes
Total Solo HM: 13
Id: 258
League: ETH
Rank: 85/111
Findings: 2
Award: $21.54
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: Udsen
Also found by: 0xMirce, 0xPsuedoPandit, 0xStalin, 0xbepresent, Aymen0909, Bobface, Co0nan, GREY-HAWK-REACH, Jeiwan, John, KupiaSec, LuchoLeonel1, Nyx, Praise, RedTiger, alexweb3, bin2chen, btk, dacian, dirk_y, josephdara, keccak123, ktg, mahdirostami, markus_ether, minhtrng, ni8mare, peanuts, ptsanev, ravikiranweb3, rvierdiiev, seeques, serial-coder, shaka, teawaterwire, wangxx2026, zzzitron
2.2492 USDC - $2.25
Anyone can mint vault shares to themselves, whereas it is supposed to be minted solely to the Yield Fee recipient(yieldFeeRecipient_
)
taking a look at the constructor, i see yieldFeeRecipient_
. as one of the constructor's input
constructor( IERC20 asset_, string memory name_, string memory symbol_, TwabController twabController_, IERC4626 yieldVault_, PrizePool prizePool_, address claimer_, address yieldFeeRecipient_,//here uint256 yieldFeePercentage_, address owner_ ) ERC4626(asset_) ERC20(name_, symbol_) ERC20Permit(name_) Ownable(owner_) {
This means the Vault.sol has a default yield fee recipient set by the protocol themselves.
AND also taking a look at the comment above the mintYieldFee
function, i noticed the _recipient
is supposed to be the yield Fee Recipient set by the protocol in the constructor when deploying vault.sol.
/** * @notice Mint Vault shares to the yield fee `_recipient`. * @dev Will revert if the Vault is undercollateralized * or if the `_shares` are greater than the accrued `_yieldFeeTotalSupply`. * @param _shares Amount of shares to mint * @param _recipient Address of the yield fee recipient */
But the issue here is that there's no check to ensure that the address passed into mintYieldFee
function as _recipient
is the same address set by the protocol as yieldFeeRecipient_
function mintYieldFee(uint256 _shares, address _recipient) external { _requireVaultCollateralized(); if (_shares > _yieldFeeTotalSupply) revert YieldFeeGTAvailable(_shares, _yieldFeeTotalSupply); _yieldFeeTotalSupply -= _shares; _mint(_recipient, _shares); emit MintYieldFee(msg.sender, _recipient, _shares); }
Therefore anyone can mint vault shares to themselves.
Manual Review
put a check to ensure that the address passed into mintYieldFee
function as _recipient
is the same address set by the protocol as yieldFeeRecipient_
Access Control
#0 - c4-judge
2023-07-14T22:21:01Z
Picodes marked the issue as duplicate of #396
#1 - c4-judge
2023-08-05T22:04:11Z
Picodes marked the issue as satisfactory
🌟 Selected for report: Udsen
Also found by: 0x11singh99, 0xPsuedoPandit, Daniel526, Darwin, Inspecktor, Jorgect, Nyx, Praise, Tripathi, YY, catellatech, namx05, squeaky_cactus, xuwinnie
19.2867 USDC - $19.29
Attacker can drain tokens from the reserve.
This is due to setDrawManager()
function having no access control implemented
function setDrawManager(address _drawManager) external { if (drawManager != address(0)) { revert DrawManagerAlreadySet(); } drawManager = _drawManager; emit DrawManagerSet(_drawManager); }
An attacker can override the address set in the constructor as drawManager to his own address via the setDrawManager() function that has no access control and bypass the access control on withdrawReserve() function. Thereby draining the funds from the reserve.
function withdrawReserve(address _to, uint104 _amount) external onlyDrawManager { if (_amount > _reserve) { revert InsufficientReserve(_amount, _reserve); } _reserve -= _amount; _transfer(_to, _amount); emit WithdrawReserve(_to, _amount); }
Manual Review
Add access control to setDrawManager() function.
Access Control
#0 - c4-judge
2023-07-14T22:59:24Z
Picodes marked the issue as duplicate of #356
#1 - c4-judge
2023-08-06T10:31:36Z
Picodes changed the severity to 2 (Med Risk)
#2 - c4-judge
2023-08-06T10:32:25Z
Picodes marked the issue as satisfactory
🌟 Selected for report: Udsen
Also found by: 0x11singh99, 0xPsuedoPandit, Daniel526, Darwin, Inspecktor, Jorgect, Nyx, Praise, Tripathi, YY, catellatech, namx05, squeaky_cactus, xuwinnie
19.2867 USDC - $19.29
The setDrawManager() can be used to override the drawManager
set in the constructor, because it has no access control implemented on it.
A malicious actor/ attacker could use this to takeover this role(drawManager
) and have access to functions that are only callable by the role (drawManager
)
This is possible since the setDrawManager() has no access control implemented on it
Manual Review
Add access control on the setDrawManager() function.
Access Control
#0 - c4-judge
2023-07-16T14:59:17Z
Picodes marked the issue as duplicate of #356
#1 - c4-judge
2023-08-06T10:32:23Z
Picodes marked the issue as satisfactory