This agent detects when someone with the executor role could escalate privileges and become an admin of the TimelockController. The agent is separated into four threads:
- First thread detects the
RoleGranted(bytes32,address,address)event for the executor and creates an alert when the executor gets the proposer or admin roles. - Second thread detects when the TimelockController vulnerability is exploited as it is described in the post-mortem.
- Third thread detects untrusted executors as it is described in the post-mortem.
- Fourth thread detects the
RoleRevoked(bytes32,address,address)event and provides alerts in 3 different situations:- The contract lost its
Adminrole. - The function
renounceRole()was used to revoke its own role. - There is common
RoleRevoked(bytes32,address,address)event.
- The contract lost its
- Ethereum
You can specify roles signatures in the src/utils.py:
class Roles(Enum):
admin = "0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5"
executor = "0xd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63"
proposer = "0xb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1"TIMELOCK-ZERO-DELAY- Fired when
MinDelayChange(uint256,uint256)event hasnewDurationargument equal to zero - this means that someone set TimelockController Minimum Delay to zero - Severity is always set to
Info - FindingType is always set to
Suspicious - Metadata:
contract- TimelockController addresstx_hash- hash of the transactionold_delay- previous delay
- Fired when
TIMELOCK-EXPLOIT- Fired when malicious executor set Minimum Delay to zero, executes the proposal and after that schedules the proposal with the same id
- Severity is always set to
Critical - FindingType is always set to
Exploit - Metadata:
contract- TimelockController addresstx_hash- hash of the transactionexecutor- executor addressproposal_id- proposal id
TIMELOCK-PROPOSAL-LIFECYCLE-VIOLATION- Fired when proposal was scheduled after the execution, but Minimum Delay was not changed
- Severity is always set to
Critical - FindingType is always set to
Suspicious - Metadata:
contract- TimelockController addresstx_hash- hash of the transactionexecutor- executor addressproposal_id- proposal id
TIMELOCK-EXECUTOR-PROPOSER- Fired when executor gets the proposal role
- Severity is always set to
Medium - FindingType is always set to
Suspicious - Metadata:
contract- TimelockController addresstx_hash- hash of the transactionexecutor- executor addressinitiator- initiator address
TIMELOCK-EXECUTOR-ADMIN- Fired when executor gets the admin role
- Severity is always set to
Critical - FindingType is always set to
Suspicious - Metadata:
contract- TimelockController addresstx_hash- hash of the transactionexecutor- executor addressinitiator- initiator address
TIMELOCK-UNTRUSTED-EXECUTOR- Fired when executor has not Proposer role
- Severity is always set to
High - FindingType is always set to
Suspicious - Metadata:
contract- TimelockController addressexecutor- executor address
TIMELOCK-ADMIN-REVOKED- Fired when contract loses its
Adminrole - Severity is always set to
High - FindingType is always set to
Suspicious - Metadata:
contract- TimelockController addresstx_hash- hash of the transactionfrom- transaction sender address
- Fired when contract loses its
TIMELOCK-ROLE-RENOUNCED- Fired when address renounced its own role
- Severity is always set to
Medium - FindingType is always set to
Info - Metadata:
contract- TimelockController addresstx_hash- hash of the transactionfrom- transaction sender addressrole- hex-role
TIMELOCK-ROLE-REVOKED- Fired when there is normal
RoleRevokeevent in the log - Severity is always set to
Medium - FindingType is always set to
Info - Metadata:
contract- TimelockController addresstx_hash- hash of the transactionfrom- transaction sender addressrole- hex-roleaccount- target account
- Fired when there is normal
- Python:
3.10
npm startThis attack has not, as far as we know, been executed on chain.
You can test the agent using
npm testThere are 11 test that should pass:
test_returns_main_exploit_finding()test_dont_return_main_finding_if_executed_after_schedule()test_return_only_proposal_lifecycle_violation_finding_if_min_delay_was_not_changed()test_return_zero_delay_finding()test_return_executor_got_proposer_role_finding()test_return_executor_got_admin_role_finding()test_returns_untrusted_executor_finding()test_returns_zero_findings_if_executor_has_proposer_role_and_everything_is_ok()test_return_contact_lost_selfadministration_finding()test_return_renounced_finding()test_return_role_revoked_finding()
For these purposes, web3 and event mocks are used. You can check web3 mock in src/test/web3_mock.py
and events mocks in src/test/agent_test.py
