sherlock-2025-04-burve-m02

[M-02] Simplex ownership cannot be transferred

보고서

Summary

BaseAdminFacet.transferOwnership 함수는 2step 으로 구현되었으므로 BaseAdminFacet.acceptOwnership 함수 역시 다이아몬드 프록시에 등록해야 한다. 하지만 이를 등록하지 않으므로 관리자 변경이 불가하다.

Keyword

proxy, multi-facet proxy, diamond proxy

Vulnerability

BaseAdminFacet.transferOwnership 함수는 2step 으로 구현되었다. 따라서 BaseAdminFacet.acceptOwnership 함수 시그니처도 등록되어야 한다.

function transferOwnership(address _newOwner) external override {
    AdminLib.reassignOwner(_newOwner);
}
 
function reassignOwner(address newOwner) internal {
    validateOwner();
@>  adminStore().pendingOwner = newOwner;
}

하지만 Diamond를 초기화할 때, BaseAdminFacet.acceptOwnership 는 등록되지 않는다. 따라서 관리자를 변경하는 기능은 이 함수를 추가하기 전까지는 동작하지 않는다.

{
    bytes4[] memory adminSelectors = new bytes4[](3);
@>  adminSelectors[0] = BaseAdminFacet.transferOwnership.selector;
    adminSelectors[1] = BaseAdminFacet.owner.selector;
    adminSelectors[2] = BaseAdminFacet.adminRights.selector;
    cuts[2] = FacetCut({
        facetAddress: address(new BaseAdminFacet()),
        action: FacetCutAction.Add,
        functionSelectors: adminSelectors
    });
}

Impact

관리자를 변경하는 기능은 BaseAdminFacet.acceptOwnership를 다이아몬드 프록시에 등록하기 전까지는 동작하지 않는다.

Mitigation

다이아몬드 프록시 생성자에서 BaseAdminFacet.acceptOwnership 함수를 등록한다.

{
-   bytes4[] memory adminSelectors = new bytes4[](3);
+   bytes4[] memory adminSelectors = new bytes4[](4);
    adminSelectors[0] = BaseAdminFacet.transferOwnership.selector;
    adminSelectors[1] = BaseAdminFacet.owner.selector;
    adminSelectors[2] = BaseAdminFacet.adminRights.selector;
+   adminSelectors[3] = BaseAdminFacet.acceptOwnership.selector;
    cuts[2] = FacetCut({
        facetAddress: address(new BaseAdminFacet()),
        action: FacetCutAction.Add,
        functionSelectors: adminSelectors
    });
}

tags: bughunting, burve, smart contract, solidity, severity medium, proxy pattern, multi-facet proxy