Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: medium
Invalid

Lack of Validation in RootProxy(initParams) Call (Potential Unauthorized Execution & Malicious Contract Injection)

1. Summary

The MarketMakingEngine contract inherits from RootProxy and passes InitParams memory initParams to its constructor. However, the structure and contents of InitParams are not validated, leading to potential security risks, such as:

  1. Unauthorized Execution – If InitParams allows arbitrary function calls, attackers might execute unintended transactions.

  2. Malicious Contract Injection – If InitParams contains unverified addresses, they could point to malicious contracts that hijack execution.

  3. Delegatecall Exploitation – If InitParams allows delegatecalls to untrusted addresses, attackers could exploit it to execute arbitrary code.

To mitigate these risks, InitParams must be validated before use.


3. Vulnerability Details

Issue

The constructor of MarketMakingEngine.sol initializes RootProxy as follows:

constructor(InitParams memory initParams) RootProxy(initParams) { }

However, the contents of InitParams are unknown at the contract level. If InitParams is manipulated to include:

  • External contract addresses (which may be malicious).

  • delegatecall operations (which can lead to contract takeover).

  • Improperly initialized storage structures (causing unexpected behavior).

This could lead to loss of control over the contract, loss of user funds, or unintended function executions.


4. Root Cause

  • Lack of explicit validation on InitParams before passing it to RootProxy.

  • Possible external dependencies (RootProxy, UpgradeBranch) that could introduce delegatecall or untrusted execution flows.

  • No access control checks or sanitization of InitParams.


https://github.com/Cyfrin/2025-01-zaros-part-2/blob/35deb3e92b2a32cd304bf61d27e6071ef36e446d/src/market-making/MarketMakingEngine.sol#L25


5. Impact

Severity Impact
** Medium** Potential contract takeover if InitParams allows delegatecalls to attacker-controlled addresses.
** Medium** Malicious contract injection if InitParams includes unverified contract addresses.
** Low** Unexpected execution paths, leading to unexpected state changes.

6. Tools Used

  • Hardhat (for testing the vulnerability)

  • Slither (for static analysis)

  • Mythril (for symbolic execution)

  • Manual code review


7. Proof of Concept (PoC)

Simulation of the Issue Using Hardhat

Step 1: Setup the Hardhat Environment

Ensure you have Hardhat installed in your project. If not, run:

npm install --save-dev hardhat

Create a test file inside test/MarketMakingEngine.js

Step 2: Deploy a Malicious Contract

a malicious contract (MaliciousInitParams.sol) that exploits the InitParams vulnerability.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
contract MaliciousInitParams {
function executeAttack() external {
selfdestruct(payable(msg.sender)); // Destroys contract and sends funds to attacker
}
}

Step 3: Write the Hardhat Test

test case in test/MarketMakingEngine.js to simulate an attack.

const { ethers } = require("hardhat");
const { expect } = require("chai");
describe("MarketMakingEngine - InitParams Vulnerability", function () {
let MarketMakingEngine, marketMakingEngine, MaliciousInitParams, maliciousContract, owner, attacker;
before(async function () {
[owner, attacker] = await ethers.getSigners();
// Deploy Malicious Contract
MaliciousInitParams = await ethers.getContractFactory("MaliciousInitParams");
maliciousContract = await MaliciousInitParams.connect(attacker).deploy();
// Deploy MarketMakingEngine with Malicious InitParams
const fakeInitParams = {
maliciousAddress: maliciousContract.address,
};
MarketMakingEngine = await ethers.getContractFactory("MarketMakingEngine");
marketMakingEngine = await MarketMakingEngine.connect(attacker).deploy(fakeInitParams);
});
it("Should allow attacker to inject malicious contract", async function () {
expect(await marketMakingEngine.maliciousAddress()).to.equal(maliciousContract.address);
});
it("Should allow attacker to execute malicious function", async function () {
await expect(maliciousContract.executeAttack())
.to.emit(maliciousContract, "Destroyed") // Checking for self-destruct event
.withArgs(attacker.address);
});
});

** Outcome**

If the test fails, it means InitParams is being properly validated.
If the test passes, it means an attacker can inject malicious contracts into InitParams and execute arbitrary code.


8. Mitigation

** Solution: Validate InitParams Before Use**

Modify the constructor to check for untrusted addresses before passing InitParams to RootProxy:

constructor(InitParams memory initParams) RootProxy(initParams) {
require(initParams.someAddress != address(0), "Invalid address");
require(isWhitelisted(initParams.someAddress), "Untrusted contract");
}

** Implement a Whitelist Mechanism**

Ensure InitParams contracts are from trusted sources only:

mapping(address => bool) public whitelist;
function isWhitelisted(address addr) internal view returns (bool) {
return whitelist[addr];
}
function addToWhitelist(address addr) external onlyOwner {
whitelist[addr] = true;
}

** Use an Allowlist Instead of delegatecall**

If InitParams includes function calls, ensure they are restricted to specific functions and not arbitrary calls.

Updates

Lead Judging Commences

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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