Skip to content

initialize function in L2GraphToken.sol, BridgeEscrow.sol, L2GraphTokenGateway.sol, L1GraphTokenGateway.sol can be invoked multiple times from the implementation contract. #149

@code423n4

Description

@code423n4

Lines of code

https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/l2/gateway/L2GraphTokenGateway.sol#L87
https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/l2/token/L2GraphToken.sol#L48
https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/gateway/L1GraphTokenGateway.sol#L99
https://github.com/code-423n4/2022-10-thegraph/blob/309a188f7215fa42c745b136357702400f91b4ff/contracts/gateway/BridgeEscrow.sol#L20

Vulnerability details

Impact

initialize function in L2GraphToken.sol, BridgeEscrow.sol, L2GraphTokenGateway.sol, L1GraphTokenGateway.sol

can be invoked multiple times from the implementation contract.

this means a compromised implementation can reinitialize the contract above and

become the owner to complete the privilege escalation then drain the user's fund.

Usually in Upgradeable contract, a initialize function is protected by the modifier

 initializer

to make sure the contract can only be initialized once.

Proof of Concept

Provide direct links to all referenced code in GitHub. Add screenshots, logs, or any other relevant proof that illustrates the concept.

  1. The implementation contract is compromised,

  2. The attacker reinitialize the BridgeEscrow contract

    function initialize(address _controller) external onlyImpl {
        Managed._initialize(_controller);
    }

the onlyGovernor modifier's result depends on the controller because

    function _onlyGovernor() internal view {
        require(msg.sender == controller.getGovernor(), "Caller must be Controller governor");
    }
  1. The attacker have the governor access to the BridgeEscrow,

  2. The attack can call the approve function to approve malicious contract

     function approveAll(address _spender) external onlyGovernor {
        graphToken().approve(_spender, type(uint256).max);
    }
  1. The attack can drain all the GRT token from the BridgeEscrow.

Tools Used

Manual Review

Recommended Mitigation Steps

We recommend the project use the modifier

 initializer

to protect the initialize function from being reinitiated

   function initialize(address _owner) external onlyImpl initializer  {

Metadata

Metadata

Assignees

No one assigned

    Labels

    2 (Med Risk)Assets not at direct risk, but function/availability of the protocol could be impacted or leak valuebugSomething isn't workingedited-by-wardenresolvedFinding has been patched by sponsor (sponsor pls link to PR containing fix)selected for reportThis submission will be included/highlighted in the audit reportsponsor confirmedSponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions