Beginner FriendlyFoundryDeFiOracle
100 EXP
View results
Submission Details
Severity: high
Invalid

Front-Running Vulnerability in Contract Initialization

Vulnerability Details

Both ThunderLoan.sol and ThunderLoanUpgraded.sol utilize an upgradeable proxy pattern which necessitates the use of initialization functions instead of constructors. The initialize functions are designed to set up the contract state for the first time.

However, these functions are marked as external and guarded only by the initializer modifier. While this modifier is intended to ensure that the function can only be called once, it does not prevent the function from being called by any external entity before the rightful owner has the chance to do so. This opens up the possibility for an attacker to front-run the transaction that sets up the contract, allowing them to initialize the contract with potentially malicious intent.

For instance, the initialize function in ThunderLoanUpgraded.sol sets important contract parameters, such as the fee structure and the address of related contracts tswapAddress:

File: ThunderLoanUpgraded.sol
function initialize(address tswapAddress) external initializer {
__Ownable_init();
__UUPSUpgradeable_init();
__Oracle_init(tswapAddress);
s_flashLoanFee = 3e15; // 0.3% ETH fee
}

The same vulnerability is present in the initial ThunderLoan.sol contract:

File: ThunderLoan.sol
function initialize(address tswapAddress) external initializer {
__Ownable_init();
__UUPSUpgradeable_init();
__Oracle_init(tswapAddress);
s_feePrecision = 1e18;
s_flashLoanFee = 3e15; // 0.3% ETH fee
}

Impact

If an attacker successfully front-runs the initialization transaction, they could manipulate critical contract parameters, as the tswapAddress address parameter which would set the s_poolFactory = poolFactoryAddress in OracleUpgradeable.sol to a potentially malicious contract address owned by the attacker.
This could lead to loss of funds or denial of service and significantly damage the protocol's credibility and trustworthiness.

Recommendations

To mitigate this risk, the ThunderLoan protocol should employ a deployment strategy that prevents the possibility of front-running. One effective approach is the use of a factory pattern where the initialization function can only be called by the factory contract, which in turn is controlled by the protocol's deployer. Alternatively, the deployment process could include measures such as using private transactions or gas price strategies to make front-running less feasible.

Short term, the initialization function should be restricted to being called by only a predetermined address, such as the deployer's address. This can be enforced by adding an additional condition in the initialize function that checks if the sender of the transaction (msg.sender) is the deployer or a specific authorized address.

In the long term, consider automating the deployment and initialization process through a secure factory contract or an initialization script that minimizes the window for front-running opportunities.

Here is an example of how the initialize function could be modified to prevent unauthorized access:

File: ThunderLoan.sol / ThunderLoanUpgraded.sol
function initialize(address tswapAddress) external {
require(msg.sender == deployerAddress, "Caller is not the deployer");
// existing initialization code...
}

The deployerAddress should be set to the address of the account that deploys the contract and is intended to run the initialization. This simple check ensures that only the deployer can initialize the contract, thereby closing the vulnerability to front-running attacks.

Updates

Lead Judging Commences

0xnevi Lead Judge
almost 2 years ago
0xnevi Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Front-running initializers

Support

FAQs

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