hats-finance / Thorn-protocol-0x1286ecdac50215a366458a14968fbca4bd95067d

GNU General Public License v3.0
0 stars 0 forks source link

Lack of access control in initialize function #75

Open hats-bug-reporter[bot] opened 1 month ago

hats-bug-reporter[bot] commented 1 month ago

Github username: @Coriolan-dev Twitter username: 0xc0c0_ Submission hash (on-chain): 0x67e3dbd79b70327cb73bd01409e10b410dab4a35c156f02a5bc961093aa3eae3 Severity: high

Description: Description

In the StableSwapFactory contract, the initialize function does not have proper protection, which allows it to be recalled by anyone. This lack of access control presents a critical security risk, as anyone can become the admin and modify important contract variables like the addresses of the LPFactory, SwapTwoPoolDeployer, and SwapThreePoolDeployer. This can lead to unauthorized control of the AMM (Automated Market Maker) system and manipulation of its core components.

Attack Scenario

An attacker could call the initialize function after the contract has been deployed and overwrite the critical variables such as the admin address. By doing so, they can become the new admin of the system and override existing token pairs with a malicious LP Token. Because the StableSwapRouter uses the getStableInfo of the StableSwapFactory, the users will swap on malicious liquidity pool.

This is an example but at the end the attacker can break everything.

Attachments

  1. Proof of Concept (PoC) File

For my tests I change the defaultNetwork value of the config file to hardhat: defaultNetwork: "hardhat".

In the Mock folder, add the MaliciousStableSwapLPFactory.sol file.

In the tests folder, add the POC.test.ts file.

Then run npx hardhat test tests/POC.test.ts

Here is the output:

Legit admin:  0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Malicious admin:  0x70997970C51812dc3A010C7d01b50e0d17dc79C8
Legit LP Contract for WROSE/ROSE pair:  0xEed2C4bCC26390945BF692656F6fAB4ef66250DE
Malicious LP Contract for WROSE/ROSE pair:  0x994e1179Cf5920773630cc4E272f8850F9Da820A
  1. Revised Code File (Optional)

Instead of using an initialize function that can be called at any time, the contract should set these variables securely through the constructor, which is only called once when the contract is deployed. Since the contract is not upgradable and does not use a proxy, this approach ensures that the admin and other critical variables are set only once and cannot be overwritten.

constructor( 
    IStableSwapLPFactory _LPFactory,
    IStableSwapDeployer _SwapTwoPoolDeployer,
    IStableSwapDeployer _SwapThreePoolDeployer,
    address _admin
) {
    LPFactory = _LPFactory;
    SwapTwoPoolDeployer = _SwapTwoPoolDeployer;
    SwapThreePoolDeployer = _SwapThreePoolDeployer;
    admin = _admin;
}

Files: