Open hats-bug-reporter[bot] opened 1 week ago
We are aware of the risk that asset managers can send calldata to the vault to execute any transaction from there. However, there are checks in place to ensure that the token balance of the reward token increases and the portfolio token balances don't increase.
Adding a check to verify that the calldata doesn't include those functions would not be effective. An attacker could write a contract containing the approve functions (delegatecall) to approve all tokens while simultaneously claiming the rewards, ensuring the transaction doesn't fail during the verification process.
Asset managers can also rebalance the portfolio to any tokens if there is no whitelisting, providing other ways to drain the funds. However, the asset manager should act in the best interest of the holders to manage and earn performance fees. These potential attacks are restricted to asset managers only.
@0xfuje
I'm not following the whole issue, but as balanceOf
is a view function, thus the counter will be reverted back after the call, so it will not be increased. The same initial assumption on #68
I'm not following the whole issue, but as balanceOf is a view function, thus the counter will be reverted back after the call, so it will not be increased. The same initial assumption on https://github.com/hats-finance/Velvet-Capital-0x0bb0c08fd9eeaf190064f4c66f11d18182961f77/issues/68
It was just an example, but true, however it's not hard to bypass this, since you can make any state changing call to raise the balance of the token in claimRewardsToken()
before the transfer out or approval calls
Github username: @0xfuje Twitter username: 0xfuje Submission hash (on-chain): 0xb53a7b241e8ca39cde2b0c141b81263cb0dd969db51b3814376eaa38cbafdc9e Severity: high
Description:
Impact
Description
The root of the problem is that the AM can execute any arbitrary call via
claimRewardTokens()
as long as it increases the balance of_tokenToBeClaimed
andtokenBalancesInVault
.Rebalancing.sol
-claimRewardTokens()
This opens up an attack path where the asset manager can overcome these restrictions with adding a custom token created by himself to the vault which will return a higher balance on subsequent
balanceOf
calls. Note that the_getTokenBalanceOf()
calls used in the function call.balanceOf()
of the tokens added which can return anything.VaultManager.sol
-claimRewardTokens()
Proof of Concept
Transfer out all vault holdings
MaliciousERC20
that returns higheruint256
when it's called every next time withbalanceOf()
. This way both thetokenBalancesInVaultAfter
andrewardTokenBalanceAfter
will be higher then the before variants.example of custom
balanceOf()
function:_updateWeights()
that will be called inupdateTokens()
MaliciousERC20
token via callingupdateTokens()
ERC20.transfer(assetManager, ERC20.balanceOf(portfolio))
with every deposited token in the vault viaRebalancing.claimRewardTokens()
with_tokenToBeClaimed
asMaliciousERC20
,_target
as the heldERC20
and_claimCalldata
as the encodedERC20.transfer()
Steal approved funds before deposit
USDC.approve(depositAmount, portfolio)
.transferFrom(user, portfolio)
to transfer the funds to the portfolio without the user actually depositingUSDC.transfer(assetManager, depositAmount)
call throughclaimRewardTokens()
Approve out all funds
claimRewardTokens()
:ERC20.approve(assetManager, type(uint256).max
).Vault
the AM can callERC20.safeTransferFrom(portfolio, assetManager, amount)
directly on the respectiveERC20
s with all of the tokensRecommendation
Consider to explicitly check if the
_claimCallData
is intending to call the function selector of:transfer()
-0xa9059cbb
transferFrom()
-0x095ea7b3
approve()
-0x23b872dd
and revert the call as these would attempt to steal assets from the
Vault
. Consider to check if_target
is one of the tokens of the vault and revert the call inclaimRewardTokens()
.