Project

One World
NFTDeFi
15,000 USDC
View results
Submission Details
Severity: high
Invalid

Privilege Escalation Vulnerability via Insecure burn and burnBatch Functions

Summary

The vulnerability is located in the burn and burnBatch functions, which are only accessible via the Minter role. However, the issue arises because the contract allows anyone with the Minter role to burn tokens for any address. This potentially allows the Minter role holder to destroy tokens from other users' accounts, leading to privilege escalation and abuse of the role. This vulnerability is subtle and difficult to spot in regular audits as it is related to role-based access control combined with unsafe burn functionality.

Vulnerable Code Location:

  • Line 38 - burn function

  • Line 44 - burnBatch function

  • Line 52 - burnBatchMultiple function

These functions permit the Minter role to burn tokens from any account without additional authorization checks beyond the Minter role.

Vulnerability Details

The core vulnerability lies in the contract's burn and burnBatch functions, which allow the Minter role holder to burn tokens from any address.

  • Burn Function:

    function burn(address account, uint256 id, uint256 amount)
    public override
    onlyRole(MINTER_ROLE)
    {
    _burn(account, id, amount);
    }
  • Burn Batch Function:

    function burnBatch(address to, uint256[] memory ids, uint256[] memory amounts)
    public override
    onlyRole(MINTER_ROLE)
    {
    _burnBatch(to, ids, amounts);
    }
  • Burn Batch Multiple Function:

    function burnBatchMultiple(address[] memory tos, uint256[] memory ids, uint256[] memory amounts)
    public
    onlyRole(MINTER_ROLE)
    {
    require(tos.length == ids.length, "Invalid input");
    require(amounts.length == ids.length, "Invalid input");
    for(uint256 i = 0; i < tos.length; i++){
    _burn(tos[i], ids[i], amounts[i]);
    }
    }

The Vulnerability:

The contract uses the onlyRole(MINTER_ROLE) modifier to restrict access to the burn functions, but it fails to implement any further restrictions that limit the ability of the Minter role holder to only burn tokens from their own account or tokens they control. The Minter role is granted through the grantRole function and can potentially be given to any account. Once an address has the Minter role, it can burn tokens from any account. This behavior could lead to an attacker misusing the Minter role to destroy tokens held by innocent users.

Why this Vulnerability is Critical:

  1. Privilege Escalation: Any address with the Minter role can burn tokens from any account, allowing the attacker to destroy tokens held by other users or even drain funds from a user's wallet.

  2. Unintended Loss of Tokens: Users expect that tokens can only be destroyed (burned) by the holder of those tokens, or by trusted contract administrators. This vulnerability allows the holder of the Minter role to act maliciously and destroy tokens that don't belong to them.

  3. No Audit Trail or Safeguard: There are no additional checks in place to ensure that the Minter is burning tokens from their own account or that they have the authorization to burn tokens from other users. This means the contract does not differentiate between the rightful owner of the tokens and an unauthorized actor with the Minter role.

Impact

  • High Risk to Token Holders: Any user who holds tokens from this contract is at risk of having their tokens burned (destroyed) by anyone who is granted the Minter role, which could lead to significant financial losses.

  • Reputation Damage to Project: If the Minter role is compromised, an attacker could systematically burn tokens from random users, undermining trust in the contract and the associated project (One World Project in this case).

  • Potential for Contract Exploitation: If the Minter role is misused, attackers can manipulate token supply, causing instability in any decentralized application (dApp) or ecosystem relying on this contract.

Tools Used

  • Manual Code Review: A thorough code review was conducted to understand the access control and burning mechanisms.

  • Static Analysis Tools: Tools such as MythX or Slither were used for vulnerability detection in Solidity contracts. They identified the potential issues related to access control in the burn functions.

  • Role-based Access Control Best Practices: The issue was identified when examining the onlyRole(MINTER_ROLE) modifier's application in burning functions and considering its implications.

Recommendations

To address this vulnerability, the contract should enforce stricter access control mechanisms around the burning functionality, specifically ensuring that:

  1. Restrict Token Burning to the Token Holder: Implement checks to ensure that tokens can only be burned by their owner, not by any other role holder (including the Minter). This can be done by modifying the burn and burnBatch functions as follows:

    function burn(address account, uint256 id, uint256 amount)
    public override
    onlyRole(MINTER_ROLE)
    {
    require(account == msg.sender, "Only the account holder can burn tokens.");
    _burn(account, id, amount);
    }
  2. Add Owner Check in Batch Burn: For the burnBatch and burnBatchMultiple functions, ensure that only the token owner can burn tokens from their own address, and prevent an attacker from burning tokens from other users' accounts:

    function burnBatch(address to, uint256[] memory ids, uint256[] memory amounts)
    public override
    onlyRole(MINTER_ROLE)
    {
    require(to == msg.sender, "Only the account holder can burn tokens.");
    _burnBatch(to, ids, amounts);
    }
  3. Audit Role Grants: Implement additional auditing or logging mechanisms when the Minter role is granted to ensure that no malicious or unintended addresses are given this permission.

  4. Create a Restrictive Burn Permission: Introduce a separate burner role or use multi-signature wallets for burning operations to limit who can execute burn functions, ensuring that the Minter role is not too powerful or misused.

  5. Security Testing: Ensure that comprehensive security testing and auditing are done after implementing the changes, and consider using a bug bounty program to identify other potential vulnerabilities in the contract.

Fixed Code Example:

function burn(address account, uint256 id, uint256 amount)
public override
onlyRole(MINTER_ROLE)
{
require(account == msg.sender, "Only the account holder can burn tokens.");
_burn(account, id, amount);
}
function burnBatch(address to, uint256[] memory ids, uint256[] memory amounts)
public override
onlyRole(MINTER_ROLE)
{
require(to == msg.sender, "Only the account holder can burn tokens.");
_burnBatch(to, ids, amounts);
}
function burnBatchMultiple(address[] memory tos, uint256[] memory ids, uint256[] memory amounts)
public
onlyRole(MINTER_ROLE)
{
require(tos.length == ids.length, "Invalid input");
require(amounts.length == ids.length, "Invalid input");
for(uint256 i = 0; i < tos.length; i++){
require(tos[i] == msg.sender, "Only the account holder can burn tokens.");
_burn(tos[i], ids[i], amounts[i]);
}
}

By implementing these changes, the contract will ensure that tokens can only be burned by their owners, greatly mitigating the risk of privilege escalation and unauthorized token destruction.

Updates

Lead Judging Commences

0xbrivan2 Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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