DoS Vulnerability In Rebalancer.sol: Detailed Analysis
Hey guys! Today, we're diving deep into a fascinating vulnerability found in the Rebalancer.sol contract, dubbed "Amusing Chrome Pony." This issue can cause a Denial-of-Service (DoS), disrupting the crucial process of cross-chain liquidity rebalancing. Let's break down what this means and how it works.
Summary
The sendMsg
function within Rebalancer.sol
has a flaw that can lead to valid rebalancing transactions being reverted. This happens because the function incorrectly accumulates the _amount
to transferInfo.size
for max transfer size checks, even when the transfer time window has expired. Imagine trying to send funds across different blockchains, but the system keeps saying no, even though you're within the limits. This is essentially what a DoS attack does, and in this case, it's caused by a miscalculation in the code.
Root Cause
The heart of the problem lies in how Rebalancer.sol
handles the maxTransferSize
limit. Specifically, the sendMsg
function updates currentTransferSize[_msg.dstChainId][_msg.token].size
by adding _amount
even when the time window for transfers has passed (transferSizeDeadline < block.timestamp
).
Instead of resetting the transferInfo.size
to zero and then checking if _amount
is less than _maxTransferSize
, it keeps adding to the old size. This means that if a previous transfer occurred and the time window has expired, the accumulated size can exceed the limit, even if the current transfer amount is perfectly valid. It's like trying to fit more water into a glass that's already full – it just spills over, or in this case, the transaction reverts with the error Rebalancer_TransferSizeExcedeed
.
This flawed logic leads to an incorrect enforcement of the maxTransferSize
limit, preventing legitimate rebalancing transactions from going through. To put it simply, the system is misremembering past transfers and incorrectly applying the size limits.
Internal Preconditions
Before this vulnerability can be exploited, a few things need to be in place within the Rebalancer
contract:
- Non-Zero
maxTransferSize
: The contract must be deployed with amaxTransferSize
set for a specific destination chain (dstChainId
) and token. This is done using thesetMaxTransferSize
function. Think of this as setting the maximum weight a truck can carry before it leaves the depot. - Previous Transaction: A rebalancing transaction must have already occurred, which sets
currentTransferSize[_msg.dstChainId][_msg.token].size
to a non-zero value. This is like the truck already having some cargo on board. - Expired Time Window: The
transferTimeWindow
(default is 86400 seconds, or 24 hours) needs to have expired since the last transfer. So, thetransferInfo.timestamp + transferTimeWindow
must be less than the currentblock.timestamp
. This is like waiting a day after the initial loading before trying to add more cargo.
External Preconditions
Externally, there's one primary condition that needs to be met for this vulnerability to be triggered:
- Rebalancer Interaction: A rebalancer (an entity with the
REBALANCER_EOA
role) must attempt to call thesendMsg
function with a valid_amount
. This amount should be less than themaxTransferSize
, but when added to the staletransferInfo.size
, it exceeds the limit. This is the moment when the truck driver tries to load more cargo, but the combined weight exceeds the limit due to the incorrect calculation.
Attack Path
Let's walk through a scenario to see how this vulnerability can be exploited:
- Configuration: The contract is configured with
maxTransferSize[_dstChainId][_token] = 1000 * 10^6
(e.g., for USDC) andtransferTimeWindow = 86400
seconds. This sets the maximum transfer size for USDC on a specific destination chain to 1,000 million units and the time window to 24 hours. - Initial Transfer: A rebalancer calls
sendMsg
for_dstChainId
and_token
(USDC), transferring 800 * 10^6 USDC. This setscurrentTransferSize[_dstChainId][_token].size = 800 * 10^6
andtransferInfo.timestamp = block.timestamp
. Now, 800 million USDC units have been transferred, and the timestamp of the transfer is recorded. - Time Expiry: After 86400 seconds (24 hours), the time window expires. This means
transferInfo.timestamp + 86400 < block.timestamp
is true. - Second Transfer Attempt: The rebalancer calls
sendMsg
again with_amount = 300 * 10^6
USDC, which is less than themaxTransferSize
of 1000 * 10^6. The rebalancer is trying to transfer 300 million USDC units, which should be allowed. - Incorrect Accumulation: In
sendMsg
, because the time window has expired, the conditiontransferSizeDeadline < block.timestamp
is true. Instead of resetting the size, the contract updatescurrentTransferSize[_dstChainId][_token].size += _amount
, resulting in 800 * 10^6 + 300 * 10^6 = 1100 * 10^6. The contract incorrectly adds the new amount to the old, stale size. - Transfer Failure: The check
require(transferInfo.size + _amount < _maxTransferSize, Rebalancer_TransferSizeExcedeed())
evaluates1100 * 10^6 > 1000 * 10^6
, causing the transaction to revert erroneously. Because the calculated size (1100 million) is greater than the maximum allowed size (1000 million), the transaction fails. - DoS: The rebalancer cannot complete the valid rebalancing transaction, preventing liquidity transfer to the destination chain. The rebalancer is blocked from performing a valid transfer, disrupting the liquidity balancing process.
Impact
The impact of this vulnerability is significant, primarily leading to a Denial-of-Service:
- Denial-of-Service (DoS): Valid rebalancing transactions are blocked, disrupting the protocol’s ability to maintain liquidity across chains. This is a core function of the
Rebalancer
, so any disruption here is critical. Imagine the bridges between different blockchains being blocked, preventing the smooth flow of assets. - Systemic Risk: A persistent DoS can lead to liquidity imbalances, reducing the protocol’s efficiency and potentially affecting user trust. If the rebalancing mechanism is consistently blocked, it can create imbalances in liquidity pools, making the protocol less efficient and potentially eroding user confidence.
- No Direct Fund Loss: Importantly, this issue does not cause direct loss of funds, as the
Rebalancer
cannot transfer user funds. The vulnerability is in the logic of the contract, not in the ability to move funds illicitly. - Sherlock Rules Alignment: Per Sherlock’s guidelines, this vulnerability qualifies as Medium severity. This is because it disrupts a core protocol function (rebalancing) without direct fund loss. Section III.3, which covers DoS in time-sensitive operations, further supports the Medium severity assessment.
Mitigation
The solution to this vulnerability is straightforward:
- Reset Size: Modify the
sendMsg
function to resetcurrentTransferSize[_dstChainId][_msg.token].size
to zero whentransferSizeDeadline < block.timestamp
. After resetting, check only if_amount < _maxTransferSize
. This ensures that the size is correctly calculated based on the current transfer, rather than being influenced by stale data from previous transfers.
By implementing this fix, the Rebalancer
contract will accurately enforce the maxTransferSize
limit, preventing valid rebalancing transactions from being erroneously blocked. This will restore the smooth functioning of cross-chain liquidity rebalancing, which is crucial for the protocol’s overall health and efficiency.
In conclusion, the "Amusing Chrome Pony" vulnerability highlights the importance of careful logic in smart contracts, especially when dealing with time-sensitive operations. By understanding the root cause and implementing the recommended mitigation, we can ensure the reliability and efficiency of the Malda protocol.