You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Perennial accounts can be rebalanced multiple times
Summary
An attacker can call rebalanceGroup multiple times in a single oracle version to earn multiple keeper fee.
Root Cause
In Controller.sol:223, all market in the group is settled before validation:
function _rebalanceGroup(addressowner, uint256group) internal {
// settles each markets, such that locals are up-to-date_settleMarkets(owner, group);
// determine imbalances
(, boolcanRebalance, Fixed6[] memoryimbalances) =checkGroup(owner, group);
if (!canRebalance) revertControllerGroupBalancedError();
IAccount account =IAccount(getAccountAddress(owner));
// pull collateral from markets with surplus collateralfor (uint256 i; i < imbalances.length; i++) {
IMarket market = groupToMarkets[owner][group][i];
if (Fixed6.unwrap(imbalances[i]) <0) account.marketTransfer(market, imbalances[i]);
}
// push collateral to markets with insufficient collateralfor (uint256 i; i < imbalances.length; i++) {
IMarket market = groupToMarkets[owner][group][i];
if (Fixed6.unwrap(imbalances[i]) >0) account.marketTransfer(market, imbalances[i]);
}
emitGroupRebalanced(owner, group);
}
But, when an account is rebalancable, calling Controller.rebalanceGroup() will simply push an order to Market, and this order must wait next oracle version to get executed: Market.sol:730
If rebalance is called multiple times in an oracle version, Market.settle will simply skip the settlement of prior orders. So when Controller checks if the account is rebalancable, the state of current oracle version will always be used:
Attacker can receive many times of keeper fee for the rebalance, victims directly lose funds from perennial account. The loss depends on marketParameter.maxPendingLocal, which is a protocol-specific param.
PoC
No response
Mitigation
Controller.rebalanceGroup should introduce an oracle version check: an account can only be settled once in one oracle version.
The text was updated successfully, but these errors were encountered:
sherlock-admin3
changed the title
Joyous Ivory Ostrich - Perennial accounts can be rebalanced multiple times
Oblivionis - Perennial accounts can be rebalanced multiple times
Sep 23, 2024
Oblivionis
High
Perennial accounts can be rebalanced multiple times
Summary
An attacker can call
rebalanceGroup
multiple times in a single oracle version to earn multiple keeper fee.Root Cause
In
Controller.sol:223
, all market in the group is settled before validation:But, when an account is rebalancable, calling
Controller.rebalanceGroup()
will simply push an order to Market, and this order must wait next oracle version to get executed:Market.sol:730
If
rebalance
is called multiple times in an oracle version,Market.settle
will simply skip the settlement of prior orders. So when Controller checks if the account is rebalancable, the state of current oracle version will always be used:At this point, when the attacker tries to rebalance again, all validations will pass until
marketParameter.maxPendingLocal
is reached.Internal pre-conditions
Victims use perennial account and set a valid rebalance group.
External pre-conditions
User's rebalance group is rebalancable -- which means one/some markets is outside the rebalance threshold:
Attack Path
Controller.rebalanceGroup()
many times, untilmaxPendingLocal
is reached:perennial-v2/packages/perennial/contracts/libs/InvariantLib.sol:86
Impact
Attacker can receive many times of keeper fee for the rebalance, victims directly lose funds from perennial account. The loss depends on
marketParameter.maxPendingLocal
, which is a protocol-specific param.PoC
No response
Mitigation
Controller.rebalanceGroup
should introduce an oracle version check: an account can only be settled once in one oracle version.The text was updated successfully, but these errors were encountered: