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:
upgradeTo
function of the proxy https://dashboard.tenderly.co/tx/mainnet/0x618d6d6bdaa4be3257aa4c695f9c10806e261f0e9759fc3133a5798fed43c062?trace=0initialize
function of the implementation https://dashboard.tenderly.co/tx/mainnet/0x3c5629d35e75eb6b1e3e17e73ea16f3638c46a120a1f31bb8988f9db89bf483a?trace=0transferOwnership
function of the implementation https://dashboard.tenderly.co/tx/mainnet/0x9e6c119e7acaa30e62ef7bc12096dc6bd74ea38b1341aa2ecaeb9092c1e37a01?trace=0claimOwnership
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:
the owner of the implementation (not of the proxy, which is stored in a different slot)
the loopring address
genesisMerkleRoot
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