Skip to content

Mcxo Buggggggs...Too many bugs Found #22

@mirmohmmadluqman

Description

@mirmohmmadluqman

SPBEAMHaltSpell.sol - Security Audit Report

High Severity Vulnerabilities

1. Missing Access Control Verification in Emergency Actions

Severity: High

Description:
The _emergencyActions() function relies entirely on inherited access control from DssEmergencySpell without any explicit verification. While the function is marked as internal and override, there's no guarantee that the parent contract implements proper authorization checks. If DssEmergencySpell doesn't properly restrict who can trigger emergency actions, any unauthorized party could potentially halt the SPBEAM module, causing a denial of service to the protocol.

Impact:
Unauthorized parties could halt critical SPBEAM functionality, disrupting the MakerDAO stability parameter module and potentially affecting protocol operations.

Attack Scenario:

  1. Attacker calls the public/external function in DssEmergencySpell
  2. This triggers _emergencyActions() in SPBEAMHaltSpell
  3. SPBEAM gets halted without authorization
  4. Protocol functionality is disrupted

Affected Code:

function _emergencyActions() internal override {
    spbeamMom.halt(address(spbeam));
    emit Halt();
}

Proposed Fix:

function _emergencyActions() internal override {
    // Add explicit verification that caller is authorized
    require(msg.sender == authorizedGovernance, "Unauthorized");
    // Or verify through parent contract
    require(isAuthorized(msg.sender), "Not authorized to execute emergency spell");
    
    spbeamMom.halt(address(spbeam));
    emit Halt();
}

2. Unvalidated External Contract Addresses from Registry

Severity: High

Description:
The contract retrieves critical addresses for spbeamMom and spbeam from _log registry at deployment time without any validation. If the _log registry is compromised, returns zero addresses, or returns addresses of malicious contracts, the spell could either fail silently or interact with unintended contracts. The immutable nature of these addresses means any issues cannot be corrected post-deployment.

Impact:
Contract could be deployed with invalid addresses leading to permanent failure of emergency mechanisms, or worse, interaction with malicious contracts that could exploit the emergency spell privileges.

Attack Scenarios:

Scenario 1: Zero Address

  • If _log.getAddress("SPBEAM_MOM") returns address(0)
  • Contract deploys successfully
  • _emergencyActions() will revert when calling address(0).halt()
  • Emergency spell becomes permanently unusable

Scenario 2: Malicious Contract

  • Attacker compromises _log before deployment
  • _log.getAddress("SPBEAM_MOM") returns attacker's contract
  • Emergency spell calls attacker.halt()
  • Attacker can execute arbitrary logic

Affected Code:

SPBEAMMomLike public immutable spbeamMom = SPBEAMMomLike(_log.getAddress("SPBEAM_MOM"));
SPBEAMLike public immutable spbeam = SPBEAMLike(_log.getAddress("MCD_SPBEAM"));

Proposed Fix:

constructor() {
    address _spbeamMom = _log.getAddress("SPBEAM_MOM");
    address _spbeam = _log.getAddress("MCD_SPBEAM");
    
    require(_spbeamMom != address(0), "Invalid SPBEAM_MOM address");
    require(_spbeam != address(0), "Invalid SPBEAM address");
    
    // Optionally verify contract code hash
    require(_spbeamMom.code.length > 0, "SPBEAM_MOM not a contract");
    require(_spbeam.code.length > 0, "SPBEAM not a contract");
    
    spbeamMom = SPBEAMMomLike(_spbeamMom);
    spbeam = SPBEAMLike(_spbeam);
}

Medium Severity Vulnerabilities

3. Silent Failure Masking in done() Function

Severity: Medium

Description:
The done() function returns true for all error cases including when SPBEAMMom is not a ward, when calls revert, or when targeting non-SPBEAM contracts. This design makes it impossible to distinguish between 'successfully halted', 'cannot halt', and 'error' states. External systems relying on this function could incorrectly assume the emergency action succeeded when it actually failed.

Impact:
Governance systems or monitoring tools could incorrectly believe an emergency halt succeeded when it failed, leaving the protocol vulnerable during critical incidents.

Attack Scenario:

  1. Emergency situation occurs requiring SPBEAM halt
  2. Governance executes SPBEAMHaltSpell
  3. _emergencyActions() fails because SPBEAMMom lost ward privileges
  4. External monitoring calls done() which returns true
  5. System believes SPBEAM is halted when it's still active
  6. Vulnerability continues to be exploited

Affected Code:

function done() external view returns (bool) {
    try spbeam.wards(address(spbeamMom)) returns (uint256 ward) {
        if (ward == 0) {
            return true;  // Returns true even though halt would fail
        }
    } catch {
        return true;  // Returns true on error
    }
    
    try spbeam.bad() returns (uint256 bad) {
        return bad == 1;
    } catch {
        return true;  // Returns true on error
    }
}

Proposed Fix:

enum SpellStatus {
    NOT_EXECUTED,
    EXECUTED_SUCCESS,
    CANNOT_EXECUTE,
    ERROR
}

function status() external view returns (SpellStatus) {
    try spbeam.wards(address(spbeamMom)) returns (uint256 ward) {
        if (ward == 0) {
            return SpellStatus.CANNOT_EXECUTE;
        }
        
        try spbeam.bad() returns (uint256 bad) {
            return bad == 1 ? SpellStatus.EXECUTED_SUCCESS : SpellStatus.NOT_EXECUTED;
        } catch {
            return SpellStatus.ERROR;
        }
    } catch {
        return SpellStatus.ERROR;
    }
}

4. No Verification of SPBEAMMom Ward Status Before Execution

Severity: Medium

Description:
The _emergencyActions() function doesn't verify that SPBEAMMom has ward privileges on SPBEAM before attempting to halt. If SPBEAMMom has been de-authorized (ward removed) from SPBEAM, the halt call will revert, causing the entire emergency spell to fail. This could happen if governance removes SPBEAMMom's privileges or if there's a configuration error.

Impact:
Emergency spell could fail at a critical moment when SPBEAM needs to be halted, leaving the protocol exposed to ongoing attacks or issues.

Failure Scenario:

  1. SPBEAMMom is initially a ward of SPBEAM
  2. Governance removes SPBEAMMom ward status for maintenance
  3. Emergency occurs requiring SPBEAM halt
  4. Emergency spell is executed
  5. spbeamMom.halt() reverts because SPBEAMMom is not authorized
  6. Emergency response fails

Affected Code:

function _emergencyActions() internal override {
    spbeamMom.halt(address(spbeam));  // No check if spbeamMom is authorized
    emit Halt();
}

Proposed Fix:

function _emergencyActions() internal override {
    // Verify authorization before attempting halt
    require(spbeam.wards(address(spbeamMom)) == 1, "SPBEAMMom not authorized");
    
    spbeamMom.halt(address(spbeam));
    emit Halt();
}

// Or implement a canExecute() check
function canExecute() public view returns (bool) {
    try spbeam.wards(address(spbeamMom)) returns (uint256 ward) {
        return ward == 1;
    } catch {
        return false;
    }
}

Low Severity Vulnerabilities

5. Potential Reentrancy Through External halt() Call

Severity: Low

Description:
While the _emergencyActions() function makes an external call to spbeamMom.halt() before emitting the Halt event, there's no reentrancy protection. If SPBEAMMom is compromised or upgraded to a malicious implementation, it could reenter the spell contract during the halt() call. Although the current implementation has limited attack surface due to being internal and having no state changes after the call, future modifications or parent contract implementations could introduce vulnerabilities.

Impact:
If SPBEAMMom is malicious or compromised, it could potentially reenter the contract, though current impact is limited due to the function being internal and having no state modifications.

Attack Scenario:

// If SPBEAMMom is malicious:
contract MaliciousSPBEAMMom {
    function halt(address target) external {
        // Attempt to reenter the spell contract
        // Current impact limited, but could exploit parent contract
        ISpell(msg.sender).someFunction();
    }
}

Affected Code:

function _emergencyActions() internal override {
    spbeamMom.halt(address(spbeam));  // External call without reentrancy guard
    emit Halt();  // Event after external call
}

Proposed Fix:

// Add reentrancy protection if parent doesn't provide it
modifier nonReentrant() {
    require(!locked, "Reentrant call");
    locked = true;
    _;
    locked = false;
}

function _emergencyActions() internal override nonReentrant {
    spbeamMom.halt(address(spbeam));
    emit Halt();
}

6. Missing Event Parameters for Audit Trail

Severity: Low

Description:
The Halt event doesn't include any parameters such as the halted contract address, the caller, or timestamp. This makes it difficult to track which SPBEAM instance was halted, who initiated it, and when, reducing the effectiveness of monitoring and incident response.

Impact:
Reduced observability and difficulty in tracking emergency actions, making incident response and forensics more challenging.

Issues Without Proper Event Parameters:
Without proper event parameters, monitoring systems cannot:

  1. Identify which SPBEAM was halted if multiple exist
  2. Track who initiated the emergency action
  3. Correlate halt events with specific incidents
  4. Build proper audit trails for compliance

Affected Code:

event Halt();  // No parameters

function _emergencyActions() internal override {
    spbeamMom.halt(address(spbeam));
    emit Halt();  // Missing context
}

Proposed Fix:

event Halt(
    address indexed spbeamAddress,
    address indexed initiator,
    uint256 timestamp
);

function _emergencyActions() internal override {
    spbeamMom.halt(address(spbeam));
    emit Halt(address(spbeam), msg.sender, block.timestamp);
}

Gas Optimization

7. Gas Inefficiency in done() Function Logic

Severity: Gas Optimization

Description:
The done() function uses nested try-catch blocks that could be optimized. The function makes two separate external calls even when the first one indicates the spell cannot execute. Additionally, checking ward status when it's 0 still proceeds to check bad() status unnecessarily.

Impact:
Unnecessary gas consumption for view function calls, especially when called frequently by monitoring systems.

Scenario:
Current implementation always makes 2 external calls even when the first indicates failure. For frequent monitoring calls, this doubles gas costs unnecessarily.

Affected Code:

function done() external view returns (bool) {
    try spbeam.wards(address(spbeamMom)) returns (uint256 ward) {
        if (ward == 0) {
            return true;
        }
    } catch {
        return true;
    }
    
    try spbeam.bad() returns (uint256 bad) {
        return bad == 1;
    } catch {
        return true;
    }
}

Proposed Fix:

function done() external view returns (bool) {
    try spbeam.wards(address(spbeamMom)) returns (uint256 ward) {
        if (ward == 0) {
            return true;  // Early return, no need for second call
        }
        
        // Only check bad() if ward check passes
        try spbeam.bad() returns (uint256 bad) {
            return bad == 1;
        } catch {
            return true;
        }
    } catch {
        return true;
    }
}

Summary

Total Vulnerabilities Found: 7

  • High Severity: 2
  • Medium Severity: 2
  • Low Severity: 2
  • Gas Optimization: 1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions