# Hijacking of node operators minipool causes loss of staked funds

createMinipool lets anyone overwrite a Withdrawable/Error minipool's owner by reusing its nodeID, locking the node operator's 1000 AVAX stake.

- Payout: Contest reward
- Severity: high
- Program: GoGoPool
- Credited: 0xdeadbeef0x
- Disclosed: 2023-03-21
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2022-12-gogopool-findings/issues/213
- Library page: https://bugbountylibrary.com/r/c4%3A2022-12-gogopool%3AH-04
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:accounting-error vuln:business-logic

**Target:** MinipoolManager.createMinipool state transition / ownership
**Root cause:** createMinipool permits overwriting an existing minipool record when the state transition to Prelaunch is valid (Withdrawable/Error/Finished), but never checks that msg.sender is the existing minipool owner, and resetMinipoolData then sets owner to msg.sender.
**Steps:**
1. NodeOp creates minipool `node-123`, rialto stakes it, 14 days pass, rialto calls `recordStakingEnd` -> state Withdrawable.
2. Attacker calls `createMinipool{value: 1000 AVAX}(node-123, duration, delegationFee, avaxAssignmentRequest)`; requireValidStateTransition(Withdrawable->Prelaunch) passes and owner is set to attacker.
3. Attacker calls `cancelMinipool(node-123)` and receives the 1000 AVAX back.
4. NodeOp calls `withdrawMinipoolFunds(node-123)` -> reverts with OnlyOwner; stake is lost.
5. Attacker also withdraws own minipool funds plus rewards.
**How found:** Read the state machine in requireValidStateTransition and noticed Withdrawable/Error/Finished all permit transition to Prelaunch; then read createMinipool and saw it calls resetMinipoolData and sets owner=msg.sender on an existing nodeID without an onlyOwner check.
**Exploitation:** Attacker needs only 1000 AVAX (recoverable) and a nodeID of a victim whose minipool is Withdrawable or Error. Re-registering the victim's nodeID flips ownership, then cancelMinipool refunds the attacker's deposit while the original NodeOp is permanently locked out of withdrawing their stake. In the longer-duration variant, rialto's recreateMinipool restakes the hijacked pool and the attacker collects rewards from the victim's hardware.
**Escalation:** Can be repeated against all GoGoPool stakers within a 14-day window to drain every node operator's principal; scenario #2 lets attacker farm yield without hosting a node.
**Insight:** When a create/register function doubles as an update path keyed by a user-supplied identifier, check whether ownership is re-derived from msg.sender on the update branch; state-machine validity checks alone don't imply caller authorization.
