Reproduced Exploit
Kinetiq: rebalance request uses an outdated validator balance
Chain
Other
Category
untagged
Date
Jan 1970
Source
AuditVault
EVM Playground
Source-level debugger — step opcodes and Solidity in sync
The attack is replayed in an in-browser EVM preloaded with the exact dumped fork state. The execution tree shows every call; step by Solidity line or by opcode across all depths — source, Stack, Memory, Storage, Balances (native / ERC-20 / NFT), Transient storage and Return value stay in sync. Click a tree node, opcode, or source line to jump. No backend, no live RPC.
Source & credit. Reproduction of a public audit finding curated by AuditVault — the original finding: Kinetiq-security-review_2025-02-26. The historical source/toolchain is unavailable; this entry is documentation only and claims no executable Forge PoC.
Vulnerability classes: vuln/logic
Reproduction: a faithful minimal reproduction of the vulnerable finding — the vulnerable code is reproduced verbatim (marked
@>) with faithful minimal doubles; local deploy, no fork.
Root cause#
ValidatorManager._addRebalanceRequest persists a fixed withdrawal amount validated against the validator balance at request time; closeRebalanceRequest reuses that stale amount and never re-reads the live balance. Rewards accruing between request and close (100e18 -> 110e18) leave 10e18 permanently stuck: a full-deactivation close retrieves only the stale 100e18. (Symmetric case: a balance decrease makes the undelegation revert and freezes the request.)
function _addRebalanceRequest(address validator, uint256 withdrawalAmount) internal {
require(!_validatorsWithPendingRebalance.contains(validator), "Validator has pending rebalance");
require(withdrawalAmount > 0, "Invalid withdrawal amount");
(bool exists, uint256 index) = _validatorIndexes.tryGet(validator);
require(exists, "Validator does not exist");
require(_validators[index].balance >= withdrawalAmount, "Insufficient balance");
validatorRebalanceRequests[validator] = RebalanceRequest({validator: validator, amount: withdrawalAmount}); // @> VULN: persists a FIXED amount validated against the balance at REQUEST time; closeRebalanceRequest reuses this stale amount and never re-reads the live balance
_validatorsWithPendingRebalance.add(validator);
emit RebalanceRequestAdded(validator, withdrawalAmount);
}
Why it's exploitable here#
ValidatorManager._addRebalanceRequest persists a fixed withdrawal amount validated against the validator balance at request time; closeRebalanceRequest reuses that stale amount and never re-reads the live balance. Rewards accruing between request and close (100e18 -> 110e18) leave 10e18 permanently stuck: a full-deactivation close retrieves only the stale 100e18. (Symmetric case: a balance decrease makes the undelegation revert and freezes the request.)
Attack path#
Marked-line walkthrough (Playground)#
The EVM Playground pins each step to the exact executed source line in 0xce01759b82…:
- L52 — Setup: enumerable set backing: Setup: minimal EnumerableSet/Map doubles back the validator bookkeeping so the marked line stays byte-identical.
- L85 — Setup: HYPE staking token: Setup: the HYPE token and HyperLiquid staking double provide real delegate and undelegate transfers.
- L175 — Setup: rebalance-request event: Setup: the manager emits RebalanceRequestAdded when a withdrawal request is recorded.
- L196 — Request validated at request time: addRebalanceRequest validates withdrawalAmount against the validator's balance as it stands at request time.
- L209 — Fixed amount persisted for later: Root cause: the request stores a fixed amount; closeRebalanceRequest reuses it and never re-reads the live balance, so rewards accrued in between are left behind.
- L222 — Close reuses the stale amount: On close the validator is looked up and the stale stored amount is undelegated — 10e18 of accrued rewards stay permanently stuck.
PoC#
Registry (Foundry, local deploy — verbatim vulnerable source + harm-asserting test):
cd 58609-h-01-addrebalancerequest-may-use-outdated-balance-for-deleg_exp
forge test -vvv
The browser Playground replays the same synthetic opcode-for-opcode and measures the harm. Both gates are green (registry forge test PASS + Playground _verify-poc VERDICT: PASS).
Sources & further analysis#
Reproductions & code
- No executable Forge reproduction is claimed; the historical source/toolchain was unavailable for this finding.
- AuditVault finding: Kinetiq-security-review_2025-02-26.
- Upstream DeFiHackLabs PoC directory: src/test.
Alerts & third-party analyses
- DeFiHackLabs incident explorer: search "Kinetiq: rebalance request uses an outdated validator balance".
- Web3Sec X hacked database: search.
- Rekt leaderboard: search.
- Solodit incident search: search.
These dashboards index community alerts tweets, post-mortems, and independent write-ups. Reach them through the protocol name above to cross-check this reproduction against other analyses.