Rebalancing breaks and can corrupt the accounting if amountToProtocol or amountToChain turn negative
Summary
Derby operates with deltas, but doesn't check that resulting amounts to be allocated are positive.
This allows for possible griefing attack permanently corrupting protocol accounting.
Vulnerability Detail
Explicit casting is used for the amounts that can be negative. In this case no reverting will happen, but the corresponding values be corrupted. In this case it cannot be reversed.
Impact
Attacker can manipulate protocol to enter corrupted accounting state by pushing such allocations that resulting protocol or chain allocations turn out to be negative, be converted to a uint and either block rebalancing or corrupt Derby state.
/// @notice Step 6 push; Game pushes deltaAllocations to vaults
/// @notice Push protocol allocation array from the game to all vaults/chains
/// @param _vault Address of the vault on given chainId
/// @param _deltas Array with delta allocations where the index matches the protocolId
function pushProtocolAllocationsToVault(
uint32 _chainId,
address _vault,
int256[] memory _deltas
) external payable onlyGame {
if (_chainId == homeChain) return IVault(_vault).receiveProtocolAllocations(_deltas);
else {
bytes4 selector = bytes4(keccak256("receiveProtocolAllocationsToVault(address,int256[])"));
bytes memory callData = abi.encodeWithSelector(selector, _vault, _deltas);
xSend(_chainId, callData, 0);
}
}
/// @notice Step 6 receive; Game pushes deltaAllocations to vaults
/// @notice Receives protocol allocation array from the game to all vaults/chains
/// @param _vault Address of the vault on given chainId
/// @param _deltas Array with delta allocations where the index matches the protocolId
function receiveProtocolAllocationsToVault(
address _vault,
int256[] memory _deltas
) external onlySelf {
return IVault(_vault).receiveProtocolAllocations(_deltas);
}
Similarly, amountToChain is calcAmountToChain() result:
hyh
medium
Rebalancing breaks and can corrupt the accounting if amountToProtocol or amountToChain turn negative
Summary
Derby operates with deltas, but doesn't check that resulting amounts to be allocated are positive.
This allows for possible griefing attack permanently corrupting protocol accounting.
Vulnerability Detail
Explicit casting is used for the amounts that can be negative. In this case no reverting will happen, but the corresponding values be corrupted. In this case it cannot be reversed.
Impact
Attacker can manipulate protocol to enter corrupted accounting state by pushing such allocations that resulting protocol or chain allocations turn out to be negative, be converted to a
uint
and either block rebalancing or corrupt Derby state.Code Snippet
rebalanceCheckProtocols() uses
uint(amountToProtocol)
:https://github.com/sherlock-audit/2023-01-derby/blob/main/derby-yield-optimiser/contracts/Vault.sol#L178-L203
calcAmountToProtocol() can return negative value for
amountToProtocol
ifcurrentAllocations[_protocol]
is negative:https://github.com/sherlock-audit/2023-01-derby/blob/main/derby-yield-optimiser/contracts/Vault.sol#L209-L218
Which is renewed via setAllocation() by adding
deltaAllocations
:https://github.com/sherlock-audit/2023-01-derby/blob/main/derby-yield-optimiser/contracts/Vault.sol#L260-L264
https://github.com/sherlock-audit/2023-01-derby/blob/main/derby-yield-optimiser/contracts/Vault.sol#L178-L189
deltaAllocations
is set via receiveProtocolAllocationsInt() -> setDeltaAllocationsInt():https://github.com/sherlock-audit/2023-01-derby/blob/main/derby-yield-optimiser/contracts/Vault.sol#L396-L400
https://github.com/sherlock-audit/2023-01-derby/blob/main/derby-yield-optimiser/contracts/MainVault.sol#L350-L358
receiveProtocolAllocationsInt() is called by receiveProtocolAllocations() (
onlyXProvider
) and receiveProtocolAllocationsGuard() (onlyGuardian
):https://github.com/sherlock-audit/2023-01-derby/blob/main/derby-yield-optimiser/contracts/MainVault.sol#L343-L345
https://github.com/sherlock-audit/2023-01-derby/blob/main/derby-yield-optimiser/contracts/MainVault.sol#L441-L443
In normal workflow those are pushed from the game (step 6):
https://github.com/sherlock-audit/2023-01-derby/blob/main/derby-yield-optimiser/contracts/XProvider.sol#L398-L425
Similarly,
amountToChain
is calcAmountToChain() result:https://github.com/sherlock-audit/2023-01-derby/blob/main/derby-yield-optimiser/contracts/XChainController.sol#L309-L320
calcDepositWithdraw() uses uint256(_amountToChain) without checks:
https://github.com/sherlock-audit/2023-01-derby/blob/main/derby-yield-optimiser/contracts/XChainController.sol#L330-L343
If
_amountToChain
be negative such explicit casting can yield big garbage unit:https://docs.soliditylang.org/en/latest/types.html#explicit-conversions
This will most likely revert
pushVaultAmounts() -> calcDepositWithdraw()
call.There is also a chance that
amountToWithdraw
computation completes, but the number end up being corrupted and will be used in sendXChainAmount():https://github.com/sherlock-audit/2023-01-derby/blob/main/derby-yield-optimiser/contracts/XChainController.sol#L368-L401
Tool used
Manual Review
Recommendation
Consider controlling both values to be positive. It's needed to be done way before conversion itself, on user entry or processing stage.