58249 sc low broken two step admin handover in alchemistcurator

Submitted on Oct 31st 2025 at 17:28:01 UTC by @pikachu0203 for Audit Comp | Alchemix V3arrow-up-right

  • Report ID: #58249

  • Report Type: Smart Contract

  • Report severity: Low

  • Target: https://github.com/alchemix-finance/v3-poc/blob/immunefi_audit/src/AlchemistCurator.sol

  • Impacts:

    • Contract fails to deliver promised returns, but doesn't lose value

Description

The AlchemistCurator contract implements a two-step admin handover mechanism, but the implementation is flawed. The acceptAdminOwnership() function uses the onlyAdmin modifier, which requires the current admin to call the function, rather than the pending admin. This violates the standard two-step ownership transfer pattern and makes emergency handovers impossible if the current admin becomes unavailable or compromised.

Affected Scenarios

  • Emergency Response: When current admin key is compromised and needs immediate transfer

  • Key Loss: When current admin loses access to their private key

  • Multi-Sig Issues: When current admin is a multi-sig that becomes unavailable

  • Time-Sensitive Operations: When admin transfer needs to happen quickly

Details of the Vulnerability

Root Cause

The vulnerability exists in the acceptAdminOwnership() function:

File: src/AlchemistCurator.sol

Vulnerable Code:

The Problem

The onlyAdmin modifier checks:

This means:

  1. Current admin must call acceptAdminOwnership() (wrong)

  2. Pending admin cannot call acceptAdminOwnership() (should be able to)

Attack Flow

  1. Step 1: Current admin's key is compromised

    • Admin realizes compromise and quickly calls transferAdminOwnerShip(secureAddress)

    • pendingAdmin is set to secureAddress

    • Transaction succeeds

  2. Step 2: Compromised admin loses access or is blocked

    • Attacker takes control of admin key

    • Legitimate admin can no longer call functions

    • OR admin key is lost before completing handover

  3. Step 3: Attempt to complete handover

    • secureAddress (pending admin) tries to call acceptAdminOwnership()

    • Transaction reverts with "PD" because onlyAdmin checks msg.sender == admin

    • msg.sender is secureAddress, but admin is still the compromised address

    • Handover permanently stuck

  4. Result: Governance deadlock

    • Current admin cannot fix (key compromised/lost)

    • Pending admin cannot accept (wrong modifier)

    • No recovery possible

Proof of Concept

Test File

Create a test file at: src/test/AlchemistCuratorBrokenTwoStepPOC.t.sol

Was this helpful?