Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: low
Valid

`TimeLockController` emergency actions can be executed without time delay

Summary

The TimelockController contract implements emergency actions that can be executed immediately after scheduling, bypassing the documented 1-day delay period (EMERGENCY_DELAY).

Vulnerability Details

While the contract defines EMERGENCY_DELAY = 1 days, this delay is never enforced in the scheduleEmergencyAction() and executeEmergencyAction() functions.

This issue contradicts the protocol's documentation and security design, which states that emergency actions should have a 1-day delay. The immediate execution capability removes an important security safeguard meant to protect against malicious or compromised admin actions.

function scheduleEmergencyAction(bytes32 id) external onlyRole(EMERGENCY_ROLE) {
_emergencyActions[id] = true;
emit EmergencyActionScheduled(id, block.timestamp);
}
function executeEmergencyAction(
address[] calldata targets,
uint256[] calldata values,
bytes[] calldata calldatas,
bytes32 predecessor,
bytes32 salt
) external payable onlyRole(EMERGENCY_ROLE) nonReentrant {
bytes32 id = hashOperationBatch(targets, values, calldatas, predecessor, salt);
if (!_emergencyActions[id]) revert EmergencyActionNotScheduled(id);
delete _emergencyActions[id];
for (uint256 i = 0; i < targets.length; i++) {
(bool success, bytes memory returndata) = targets[i].call{value: values[i]}(calldatas[i]);
if (!success) {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
}
revert CallReverted(id, i);
}
}
emit EmergencyActionExecuted(id);
}

Impact

  • Emergency actions can be executed instantly without any time delay

  • Bypasses the intended 1-day cooling period for emergency actions

  • Removes the ability for users to react to potentially malicious emergency actions

  • Compromised admin could make immediate critical changes to the protocol

  • Violates the principle of time-delayed governance actions

Tools Used

Manual review

Proof of Concept

The following test case already exists in the test/unit/core/governance/proposals/TimelockController.test.js file:

it("should allow emergency action execution", async () => {
const targets = [await testTarget.getAddress()];
const values = [0];
const calldatas = [testTarget.interface.encodeFunctionData("setValue", [911])];
const operationId = await timelock.hashOperationBatch(
targets,
values,
calldatas,
ethers.ZeroHash,
ethers.ZeroHash
);
await timelock.connect(owner).scheduleEmergencyAction(operationId);
await expect(
timelock.connect(owner).executeEmergencyAction(
targets,
values,
calldatas,
ethers.ZeroHash,
ethers.ZeroHash
)
).to.emit(timelock, "EmergencyActionExecuted");
expect(await testTarget.value()).to.equal(911);
});

Recommendations

Modify the executeEmergencyAction function to enforce the EMERGENCY_DELAY and modify the _emergencyActions mapping to also store timestamps.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

TimelockController emergency actions bypass timelock by not enforcing EMERGENCY_DELAY, allowing immediate execution

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

TimelockController emergency actions bypass timelock by not enforcing EMERGENCY_DELAY, allowing immediate execution

Support

FAQs

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