Summary
Laundrette policy is not authorized to call Kernel::executeAction
function.
Vulnerability Details
The current admin of gang policy is Laundrette. As admin Laundrette can grant or revoke roles. The purpose of Laundrette::retrieveAdmin
is to change the current admin of gang policy. For any reason the god father must be able to change the current admin!
One concrete example : If he wants to prevent gang members to leave the gang.
The implementation of Laundrette::retrieveAdmin
doesn't allow the God Father to change the current admin, because only executor
can call kernel::executeAction
and the executor
is God Father
.
Laundrette.sol
function retrieveAdmin() external {
kernel.executeAction(Actions.ChangeAdmin, kernel.executor());
}
Kernel.sol
modifier onlyExecutor() {
if (msg.sender != executor) revert Kernel_OnlyExecutor(msg.sender);
_;
}
function executeAction(Actions action_, address target_) external onlyExecutor {
if (action_ == Actions.InstallModule) {
ensureContract(target_);
ensureValidKeycode(Module(target_).KEYCODE());
_installModule(Module(target_));
} else if (action_ == Actions.UpgradeModule) {
ensureContract(target_);
ensureValidKeycode(Module(target_).KEYCODE());
_upgradeModule(Module(target_));
} else if (action_ == Actions.ActivatePolicy) {
ensureContract(target_);
_activatePolicy(Policy(target_));
} else if (action_ == Actions.DeactivatePolicy) {
ensureContract(target_);
_deactivatePolicy(Policy(target_));
} else if (action_ == Actions.MigrateKernel) {
ensureContract(target_);
_migrateKernel(Kernel(target_));
} else if (action_ == Actions.ChangeExecutor) {
executor = target_;
} else if (action_ == Actions.ChangeAdmin) {
admin = target_;
}
emit ActionExecuted(action_, target_);
}
Impact
The god father won't be able to change the current admin directly from the policy
Tools Used
Manual review
Foundry unit test
Recommendations
Here’s a revised implementation that should work correctly:
Laundrette.sol: Modify the retrieveAdmin function to check if the caller is the executor and then proceed with the action.
pragma solidity ^0.8.24;
interface IKernel {
function executeAction(Actions action, address target) external;
function executor() external view returns (address);
}
enum Actions {
InstallModule,
UpgradeModule,
ActivatePolicy,
DeactivatePolicy,
MigrateKernel,
ChangeExecutor,
ChangeAdmin
}
contract Laundrette {
IKernel public kernel;
constructor(address _kernel) {
kernel = IKernel(_kernel);
}
modifier onlyExecutor() {
require(msg.sender == kernel.executor(), "Laundrette: Only executor can call this function");
_;
}
function retrieveAdmin() external onlyExecutor {
kernel.executeAction(Actions.ChangeAdmin, msg.sender);
}
}