25903 - [SC - Insight] Possible loss of user funds by front-runing the...
Submitted on Nov 20th 2023 at 23:39:49 UTC by @infosec_us_team for Boost | DeGate
Report ID: #25903
Report type: Smart Contract
Report severity: Insight
Target: https://etherscan.io/address/0x9C07A72177c5A05410cA338823e790876E79D73B#code
Impacts:
Permanent freezing of funds in the Default Deposit Contract that is less than 2,500,000 USD.
Griefing (e.g. no profit motive for an attacker, but damage to the users or the protocol)
Description
Bug description
The current deployment process for the ExchangeV3
and DefaultDepositContract
smart contracts is as follows:
Step 1- Deploy the implementation (transaction 1)
Example tx:https://etherscan.io/tx/0xb5b283c538741a1513b2f100cfd3ab4058560712e4ec0752fd587e30e23edddd
Step 2- Deploy proxy (transaction 2)
Example tx: https://etherscan.io/tx/0x989a6e990ebe16ce29a4d2e649c7ed61b05edf6870d84872109c141ef0db5f98
Step 3- Make the proxy point to the implementation (transaction 3)
Example tx: https://etherscan.io/tx/0x618d6d6bdaa4be3257aa4c695f9c10806e261f0e9759fc3133a5798fed43c062
Step 4- Initialize the implementation (transaction 4)
Example tx: https://etherscan.io/tx/0x3c5629d35e75eb6b1e3e17e73ea16f3638c46a120a1f31bb8988f9db89bf483a
They are coded in a deployment script (https://github.com/degatedev/protocols/blob/degate_mainnet/packages/loopring_v3/migrations/8_deploy_exchange_v3.js#L121). These transactions happen automatically one after the other.
When deploying and initializing, the current workflow doesn't check if the previous transaction succeeds before continuing with the next one.
The initialize(address _exchange)
function inside DefaultDepositContract
allows to set the exchange address and the owner
of the smart contract.
In the best case scenario the devs will have to re-deploy all the smart contracts once again, if the initialize(address _exchange)
is front-run with an attacker-controlled _exchange
address.
In the worst case scenario, because the deployment script does not check if the previous transaction succeeded, the address of this maliciously initialized DefaultDepositContract
may end up being used in the front-end of the site and users will directly transfer funds to it using the ERCO20 interface of the tokens (also known in DeGate as realizing an unconfirmed standard deposit), and their funds will be lock forever inside that smart contract.
Permanent fix
There are 3 ways of fixing this and we consider it is so easy to fix that doing so should not be overlooked:
Run Step 1 to Step 4 atomically.
Within the same transaction, do everything. Deploy, upgrade, and initialize the smart contracts.
Alternatively, in the script check if the previous transaction succeed before going to the next one. Stop the deployment if something failed
Not recommended, there's really not point on having to re-deploy everything if the initialization gets front-run.
Modify the
initialize(...)
function ofDefaultDepositContract
andExchangeV3
to only allow the deployer to initialize them
Proof of concept
Here's the deployment script proving that the result from the initialize(...)
transaction is not checked:
https://github.com/degatedev/protocols/blob/degate_mainnet/packages/loopring_v3/migrations/8_deploy_exchange_v3.js#L142-L147
Last updated