Core Contracts

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

Emergency revoke transfers unreleased tokens to contract instead of trusted emergency role in `RAACReleaseOrchestrator`

Summary

The emergencyRevoke function is intended to allow a trusted emergency role to revoke a beneficiary's vesting schedule and recover any unreleased tokens in emergency situations (e.g., if the contract is compromised). However, the function mistakenly transfers the unreleased tokens to the contract itself (address(this)) rather than to the caller (a trusted emergency role). This misdirection can lead to the tokens being locked within the contract, thereby hindering immediate access and reallocation during a critical emergency.

Vulnerability Details

  1. Emergency Revoke Functionality:

    • The emergencyRevoke function is designed to cancel a vesting schedule and recover any remaining tokens:

      function emergencyRevoke(address beneficiary) external onlyRole(EMERGENCY_ROLE) {
      VestingSchedule storage schedule = vestingSchedules[beneficiary];
      if (!schedule.initialized) revert NoVestingSchedule();
      uint256 unreleasedAmount = schedule.totalAmount - schedule.releasedAmount;
      delete vestingSchedules[beneficiary];
      if (unreleasedAmount > 0) {
      raacToken.transfer(address(this), unreleasedAmount); //@audit it is not transferred to the beneficiary but to this contract, why ?
      emit EmergencyWithdraw(beneficiary, unreleasedAmount);
      }
      emit VestingScheduleRevoked(beneficiary);
      }

      Instead of transferring the unreleased tokens to a trusted party (such as msg.sender or a designated recovery address), the function calls:

      raacToken.transfer(address(this), unreleasedAmount);

      which directs the tokens to the contract's own address.

  2. Intended Behavior vs. Implementation:

    • Intended Behavior:
      In an emergency, a trusted account (with the EMERGENCY_ROLE) should be able to recover the unreleased tokens immediately, allowing them to manage or reallocate the tokens appropriately.

    • Actual Behavior:
      The unreleased tokens are inadvertently transferred to address(this), leaving them locked within the contract and inaccessible to the emergency role. This undermines the purpose of having an emergency revocation mechanism.

  3. Documentation vs. Implementation Mismatch:

    • The comments in the code and the emergency functionality suggest that a recovery of tokens is necessary in critical situations. However, transferring tokens to the contract itself contradicts this goal, potentially delaying or preventing rapid emergency intervention.

Impact

  • Locked Funds:
    Unreleased tokens become stuck in the contract, rendering them inaccessible for immediate reallocation or recovery by the emergency team.

  • Delayed Emergency Response:
    In a crisis, the emergency role might be unable to promptly recover or redistribute funds, which could exacerbate the situation, especially if rapid action is required to mitigate an exploit or security breach.

Proof-of-Concept (POC) Example

Assume:

  • A beneficiary has a vesting schedule with a total allocation of 1,000 tokens, with 400 tokens already released.

  • An emergency scenario occurs, prompting the emergency role to revoke the vesting schedule.

Steps:

  1. The emergency role calls emergencyRevoke(beneficiary).

  2. The function calculates the unreleased tokens as:

    unreleasedAmount = 1,000 - 400 = 600 tokens;
  3. Instead of transferring 600 tokens to the emergency role (or a designated recovery address), the function transfers the tokens to the contract itself:

    raacToken.transfer(address(this), 600);
  4. Result:
    The 600 tokens remain locked within the contract, preventing the emergency role from accessing them during the crisis.

Tools Used

Recommendations

Correct the Transfer Destination:

  • Modify the emergencyRevoke function so that unreleased tokens are transferred to msg.sender (the emergency role executing the function) or to a pre-designated secure address:

    raacToken.transfer(msg.sender, unreleasedAmount);
  • This adjustment ensures that the tokens are immediately available for emergency management.

Updates

Lead Judging Commences

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

RAACReleaseOrchestrator::emergencyRevoke sends revoked tokens to contract address with no withdrawal mechanism, permanently locking funds

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

RAACReleaseOrchestrator::emergencyRevoke sends revoked tokens to contract address with no withdrawal mechanism, permanently locking funds

Support

FAQs

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