MultiSig Timelock

First Flight #55
Beginner FriendlyWallet
100 EXP
View results
Submission Details
Severity: medium
Valid

19Dec2025_AuditReport7_MultiSigTimelock

Root + Impact

Description

  • No Validation of Transaction Data Parameter

  • The proposeTransaction function accepts arbitrary bytes data without any validation. This could allow the owner to propose transactions that call functions on the recipient contract in unintended ways, potentially bypassing security measures of the recipient contract or causing unexpected behavior.

// Root cause in the codebase with @> marks to highlight the relevant section
// Owner could propose a transaction that calls a dangerous function
bytes memory maliciousData = abi.encodeWithSignature(
"delegatecall(address,bytes)",
attackerContract,
exploitData
);
multiSigTimelock.proposeTransaction(victimContract, 0, maliciousData);

Risk

Likelihood:

  • The proposeTransaction function accepts arbitrary bytes data without any validation. This could allow the owner to propose transactions that call functions on the recipient contract in unintended ways, potentially bypassing security measures of the recipient contract or causing unexpected behavior.

Impact:

  • Malicious or accidental inclusion of harmful data could cause recipient contracts to behave unexpectedly, potentially leading to fund loss or contract compromise at the recipient.

Proof of Concept

The Risk: Arbitrary Execution

The primary vulnerability here is Unvalidated Calldata. Because the function accepts any bytes calldata data without checking its content, an attacker (or a malicious/compromised owner) can propose a payload that causes catastrophic damage.

Key Issues:

Malicious Payloads: An attacker can craft data that calls sensitive functions like transfer(), approve(), or even selfdestruct() on other contracts held by the MultiSig.

Delegatecall Injection: As seen in your previous example, if the MultiSig’s execution logic uses delegatecall, the data could be used to overwrite the contract's entire storage, including the owner list itself.

The "Trojan Horse": Since it's a proposal, other owners might see the to address and assume it is safe, without realizing the data payload contains a hidden malicious command.

function proposeTransaction(address to, uint256 value, bytes calldata data)
external
nonReentrant
noneZeroAddress(to)
onlyOwner
returns (uint256)
{
return _proposeTransaction(to, value, data); // No validation of 'data'
}

Recommended Mitigation

Function Whitelisting

This code implements Function Selector Validation, which acts as a firewall for your MultiSig.

The Selector: The first 4 bytes of any transaction data represent the specific function being called (e.g., transfer(address,uint256)).

The Guard: By checking s_allowedSelectors, the contract ensures that only pre-approved functions can be proposed. This prevents an owner from proposing hidden malicious calls (like a delegatecall or a storage override) that haven't been vetted.

The Ether Exception: The selector == bytes4(0) check allows for simple Ether transfers (which have empty data) while still blocking complex, unauthorized contract interactions.

Why this is effective:

It limits the attack surface. Even if an owner's private key is compromised, the attacker cannot use the MultiSig to call "dangerous" functions (like changing the owner list or upgrading the contract) unless those specific function signatures were explicitly whitelisted by the DAO or developers beforehand.

// Consider adding a whitelist of allowed function selectors or implement data validation:
mapping(bytes4 => bool) private s_allowedSelectors;
function proposeTransaction(address to, uint256 value, bytes calldata data) external {
if (data.length >= 4) {
bytes4 selector = bytes4(data[:4]);
require(s_allowedSelectors[selector] || selector == bytes4(0), "Selector not allowed");
}
return _proposeTransaction(to, value, data);
}
Updates

Lead Judging Commences

kelechikizito Lead Judge 4 days ago
Submission Judgement Published
Validated
Assigned finding tags:

No validation of Tx calldata

Support

FAQs

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

Give us feedback!