26073 - [SC - Insight] The implementation upgrade must be done by call...

Submitted on Nov 24th 2023 at 04:30:05 UTC by @Paludo0x for Boost | DeGate

Report ID: #26073

Report type: Smart Contract

Report severity: Insight

Target: https://etherscan.io/address/0x9C07A72177c5A05410cA338823e790876E79D73B#code

Impacts:

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

  • Impact caused by the unintended functionality of multisig owners

Description

Bug Description

The implementation upgrade in the ExchangeV3 proxy was performed by sequentially calling:

  1. upgradeTo function of the proxy https://dashboard.tenderly.co/tx/mainnet/0x618d6d6bdaa4be3257aa4c695f9c10806e261f0e9759fc3133a5798fed43c062?trace=0

  2. initialize function of the implementation https://dashboard.tenderly.co/tx/mainnet/0x3c5629d35e75eb6b1e3e17e73ea16f3638c46a120a1f31bb8988f9db89bf483a?trace=0

  3. transferOwnership function of the implementation https://dashboard.tenderly.co/tx/mainnet/0x9e6c119e7acaa30e62ef7bc12096dc6bd74ea38b1341aa2ecaeb9092c1e37a01?trace=0

  4. claimOwnership function of the implementation https://dashboard.tenderly.co/tx/mainnet/0x238ba9b1141f396446013989c947851eba3629e81121347f6d66f167f8823469?trace=0.2.1

The issue is that upgradeTo and initialize calls have been done at different blocks (respectively 18552107 and 18552108). That imply that anyone could have front run the initialize call because it is done throug fallback function of proxy contract which is permissionless.

Function initialize is the following:

So, the initialize function initializes:

  1. the owner of the implementation (not of the proxy, which is stored in a different slot)

  2. the loopring address

  3. genesisMerkleRoot

  4. genesisMerkleAssetRoot

We must consider that the upgrade function is called when the implementation is initialized for the first time or when the implementation is updated to a new version. In the last case it should be called by the multisig and the timelock after at least 45 days. Therefore, resolving the issue could take a long time and a revert of the transaction could be unnoticed.

Impact

What could be the effects of initialization by any user?

If a malicious user set up a different owner, fortunately, this can be changed by the proxy owner but at later time if the proxy owner is the Timelock.

At the moment, I canno comment the effects of initializing with counterfeit genesisMerkleRoot and genesisMerkleAssetRoot.

Eventually, in my opinion, the biggest problem is Loopring address. A malicious user could deploy a fake Loopring contract similar to the real one but with a backdoor usable by the malicious user himself. While the other initialization parameters (owner and merkle roots) would be the equivalent to the ones of the legitimate transsaction. This situation could be unnoticed by legitimate owners.

Proof of concept

The POC verifies that any user can call initialize the function after upgrading of implementation and if the same function is called by the owner afterwards it reverts because already initialized. It shall be run with foundry with the following command forge test --match-test test_initialize_frontRun --fork-url YOUR_RPC_URL --fork-block-number 18552107

Last updated

Was this helpful?