Rock Paper Scissors

First Flight #38
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: medium
Invalid

Lack of two-step process for critical admin transfer operation

Description:

The setAdmin() function in the RockPaperScissors contract implements a single-step process for transferring admin privileges.

function setAdmin(address _newAdmin) external {
require(msg.sender == adminAddress, "Only admin can set new admin");
require(_newAdmin != address(0), "Admin cannot be zero address");
adminAddress = _newAdmin;
}

This design is error-prone since the admin role is immediately transferred to the specified address without confirmation from the new admin. If the function is called with an incorrect address (e.g., typo, miscopied address, or compromised wallet), administrative control of the contract could be permanently lost.

Attack path:

  • Current admin calls setAdmin() with an incorrect address (e.g., a mistyped address or an address for which no one has the private key)

  • The function immediately transfers admin control to the new address: adminAddress = _newAdmin;

  • Since there's no way to verify the new admin can actually control the address before the transfer, administrative access to the contract is permanently lost

  • No rollback mechanism exists to revert the change

Impact:

The loss of admin privileges would severely impact protocol management:

  • No ability to withdraw accumulated protocol fees (withdrawFees function)

  • No ability to update the join timeout setting (setJoinTimeout function)

  • No ability to transfer admin role to another address

  • Protocol fees would be permanently locked in the contract

Recommended Mitigation:

Implement a two-step ownership transfer process:

  1. Add a pendingAdmin state variable to the contract:

    address public pendingAdmin;
  2. Modify the admin transfer process to use two functions:

    function proposeAdmin(address _newAdmin) external {
    require(msg.sender == adminAddress, "Only admin can propose new admin");
    require(_newAdmin != address(0), "Admin cannot be zero address");
    pendingAdmin = _newAdmin;
    emit AdminProposed(_newAdmin);
    }
    function acceptAdmin() external {
    require(msg.sender == pendingAdmin, "Only proposed admin can accept role");
    address oldAdmin = adminAddress;
    adminAddress = pendingAdmin;
    pendingAdmin = address(0);
    emit AdminTransferred(oldAdmin, adminAddress);
    }
Updates

Appeal created

m3dython Lead Judge 2 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational

Code suggestions or observations that do not pose a direct security risk.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.